Sequence Access Method WIP

Started by Simon Riggsalmost 13 years ago124 messages
#1Simon Riggs
simon@2ndQuadrant.com
1 attachment(s)

SeqAm allows you to specify a plugin that alters the behaviour for
sequence allocation and resetting, aimed specifically at clustering
systems.

New command options on end of statement allow syntax
CREATE SEQUENCE foo_seq
USING globalseq WITH (custom_option=setting);
which allows you to specify an alternate Sequence Access Method for a
sequence object,
or you can specify a default_sequence_mgr as a USERSET parameter
SET default_sequence_mgr = globalseq;

Existing sequences can be modified to use a different SeqAM, by calling
ALTER SEQUENCE foo_seq USING globalseq;

SeqAM is similar to IndexAM: There is a separate catalog table for
SeqAMs, but no specific API to create them. Initdb creates one
sequence am, called "local", which is the initial default. If
default_sequence_mgr is set to '' or 'local' then we use the local
seqam. The local seqam's functions are included in core.

Status is still "Work In Progress". Having said that most of the grunt
work is done and if we agree the shape of this is right, its
relatively easy going code.

postgres=# select oid, * from pg_seqam;
-[ RECORD 1 ]+--------------------
oid | 3839
seqamname | local
seqamalloc | seqam_local_alloc
seqamsetval | seqam_local_setval
seqamoptions | seqam_local_options

postgres=# select relname, relam from pg_class where relname = 'foo2';
relname | relam
---------+-------
foo2 | 3839

postgres=# create sequence foo5 using global;
ERROR: access method "global" does not exist

Footprint
backend/access/Makefile | 2
backend/access/common/reloptions.c | 26 +++
backend/access/sequence/Makefile | 17 ++
backend/access/sequence/seqam.c | 278 +++++++++++++++++++++++++++++++++++++
backend/catalog/Makefile | 2
backend/commands/sequence.c | 132 +++++++++++++++--
backend/commands/tablecmds.c | 3
backend/nodes/copyfuncs.c | 4
backend/nodes/equalfuncs.c | 4
backend/parser/gram.y | 84 ++++++++++-
backend/parser/parse_utilcmd.c | 4
backend/utils/cache/catcache.c | 6
backend/utils/cache/syscache.c | 23 +++
backend/utils/misc/guc.c | 12 +
include/access/reloptions.h | 6
include/access/seqam.h | 27 +++
include/catalog/indexing.h | 5
include/catalog/pg_proc.h | 6
include/catalog/pg_seqam.h | 70 +++++++++
include/nodes/parsenodes.h | 8 -
include/utils/guc.h | 1
include/utils/rel.h | 22 +-
include/utils/syscache.h | 2
23 files changed, 706 insertions(+), 38 deletions(-)

Tasks to complete
* contrib module for example/testing
* Docs

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

seqam.v5.patchapplication/octet-stream; name=seqam.v5.patchDownload
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index c32088f..aea4a14 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,6 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = common gin gist hash heap index nbtree rmgrdesc spgist transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 456d746..14b2e98 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -262,6 +262,7 @@ static bool need_initialization = true;
 static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 					int text_len, bool validate);
+static bytea *common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate);
 
 /*
  * initialize_reloptions
@@ -796,6 +797,9 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 		case RELKIND_INDEX:
 			options = index_reloptions(amoptions, datum, false);
 			break;
+		case RELKIND_SEQUENCE:
+			options = sequence_reloptions(amoptions, datum, false);
+			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
 			break;
@@ -1203,13 +1207,31 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 /*
  * Parse options for indexes.
+ */
+bytea *
+index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for sequences.
+ */
+bytea *
+sequence_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
-bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+static bytea *
+common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..f782f7e
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..8b0c1e3
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,278 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQLStandard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each object refers to one access method, though there can
+ * be many different access methods in use if required.
+ *
+ * SQLStandard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering
+ * mechanisms from controlling behaviour. Sequence access methods are
+ * general purpose though designed specifically to address the needs of
+ * Sequences working as part of a multi-node "cluster", though that is
+ * not defined here, nor are there dependencies on anything outside of
+ * this module, nor any particular form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour,
+ * is referred to as the "Local" SeqAm. That is also the basic default.
+ * Local SeqAm assumes that allocations from the sequence will be
+ * contiguous, so if user1 requests a range of values and is given
+ * 500-599 as values for their backend then the next user to make a
+ * request will be given a range starting with 600.
+ *
+ * The SeqAm mechanism allows us to override the Local behaviour, for
+ * use with clustering systems. When multiple masters can request
+ * ranges of values it would break the assumption of contiguous
+ * allocation. It seems likely that the SeqAm would also wish to control
+ * node-level caches for sequences to ensure good performance.
+ * The CACHE option is not affected by this, since that works per backend.
+ *
+ * SeqAm allows calls to allocate a new range of values, reset the
+ * sequence to a new value and to define options for the AM module.
+ * The on-disk format of Sequences is the same for all AMs.
+ *
+ * We currently assume that there is no persistent state held
+ * within the SeqAm, so it is safe to ALTER the access method of an
+ * object without taking any special actions, as long as we hold an
+ * AccessExclusiveLock while we do that.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores
+ * the Oid of the SeqAM, just as we do for IndexAm. The relcache stores
+ * AM information in much the same way for indexes and sequences, and
+ * management of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM
+ * to decide how that is handled, for example, whether there is a higher
+ * level cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.
+ * There is no syscache for pg_seqam, though the SeqAm data is stored
+ * on the relcache entry for the sequence.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+
+char       *default_seq_mgr = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname) \
+do { \
+	procedure = &seqRelation->rd_aminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+		elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Manager API
+ *
+ *  INTERFACE ROUTINES
+ *		sequence_alloc		- allocate a new range of values for the sequence
+ *		sequence_setval		- coordinate the reset of a sequence to new value
+ *
+ *		sequence_reloptions	- process options - located in reloptions.c
+ *
+ *-------------------------------------------------------------------------
+ */
+void
+sequence_alloc(Relation seqRelation, int64 start, int64 end)
+{
+	FmgrInfo   *procedure;
+	GET_SEQAM_PROCEDURE(seqamalloc);
+
+#ifdef UNDEF
+	/*
+	 * have the seqam's alloc proc do all the work.
+	 */
+	return DatumGetBool(FunctionCall6(procedure,
+										PointerGetDatum(seqRelation),
+								        PointerGetDatum(values),
+								        PointerGetDatum(isnull),
+								        PointerGetDatum(heap_t_ctid),
+								        PointerGetDatum(heapRelation),
+								        Int32GetDatum((int32) checkUnique)));
+    FmgrInfo   *procedure;
+	Oid seqam = seqrel->relam;
+
+	Assert(RelationIsValid(seqrel));
+
+	if (!OidIsValid(seqam))
+		seqam = default_sequence_am_Oid;
+	if (!OidIsValid(seqam))
+		elog(ERROR, "invalid default_sequence_am_Oid");
+#endif
+}
+
+/*
+ * sequence_setval allows the SeqAM to disallow resetting of a sequence,
+ * if required, by returning false. If we return true then the sequence
+ * value can be reset as specified, if we hear false from the SeqAM
+ * then we throw an ERROR instead.
+ *
+ * If isLocked is set it means we have an AccessExclusiveLock on the
+ * sequence, otherwise we are being called by a setval() function.
+ */
+void
+sequence_setval(Relation seqRelation, int64 next, bool isLocked)
+{
+	FmgrInfo   *procedure;
+	bool		allowed;
+
+	GET_SEQAM_PROCEDURE(seqamsetval);
+
+
+
+	if (!allowed)
+		ereport(ERROR,
+				(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+				 errmsg("sequence setval incomplete")));
+}
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new default_sequence_mgr */
+bool
+check_default_seq_mgr(char **newval, void **extra, GucSource source)
+{
+	if (**newval == '\0')
+		return true;
+
+#ifdef UNDEF
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.	Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence manager \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence manager \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+#endif
+
+	return true;
+}
+
+#ifdef UNDEF
+/*
+ * GetDefaultSeqAM -- get the OID of the current default sequence AM
+ *
+ * This exists to hide (and possibly optimize the use of) the
+ * default_tablespace GUC variable.
+ */
+Oid
+GetDefaultTablespace(char relpersistence)
+{
+	Oid			result;
+
+	/* Fast path for default_tablespace == "" */
+	if (default_tablespace == NULL || default_tablespace[0] == '\0')
+		return InvalidOid;
+
+	/*
+	 * It is tempting to cache this lookup for more speed, but then we would
+	 * fail to detect the case where the tablespace was dropped since the GUC
+	 * variable was set.  Note also that we don't complain if the value fails
+	 * to refer to an existing tablespace; we just silently return InvalidOid,
+	 * causing the new object to be created in the database's tablespace.
+	 */
+	result = get_tablespace_oid(default_tablespace, true);
+
+	/*
+	 * Allow explicit specification of database's default tablespace in
+	 * default_tablespace without triggering permissions checks.
+	 */
+	if (result == MyDatabaseTableSpace)
+		result = InvalidOid;
+	return result;
+}
+#endif
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = LOCAL functions
+ *
+ *------------------------------------------------------------
+ */
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+void
+seqam_local_alloc(void)
+{
+	/* No actions required */
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Coordinate the setting of a local sequence.
+ */
+bool
+seqam_local_setval(Relation seqRelation, int64 next, bool isLocked)
+{
+	return true;
+}
+
+/*
+ * seqam_local_options()
+ *
+ * Verify the options of a local sequence.
+ */
+Datum
+seqam_local_options(PG_FUNCTION_ARGS)
+{
+	Datum       reloptions = PG_GETARG_DATUM(0);
+	bool        validate = PG_GETARG_BOOL(1);
+	bytea      *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+	PG_RETURN_NULL();
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index df6da1f..d923fc3 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -34,7 +34,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 1f2546d..203993f 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,11 +14,14 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -93,10 +96,12 @@ static Relation open_share_lock(SeqTable seq);
 static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
 static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
 			   Buffer *buf, HeapTuple seqtuple);
-static void init_params(List *options, bool isInit,
+static void init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by);
 static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static Oid init_options(char *accessMethod, List *options);
 
 
 /*
@@ -110,6 +115,7 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	Relation	rel;
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
@@ -124,8 +130,9 @@ DefineSequence(CreateSeqStmt *seq)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("unlogged sequences are not supported")));
 
-	/* Check and set all option values */
-	init_params(seq->options, true, &new, &owned_by);
+	/* Check and set all param values */
+	init_params(seq->params, true, &new, &owned_by);
+	seqamid = init_options(seq->accessMethod, seq->options);
 
 	/*
 	 * Create relation (and fill value[] and null[] for the tuple)
@@ -208,7 +215,8 @@ DefineSequence(CreateSeqStmt *seq)
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = list_make1(defWithOids(false));
+	stmt->options = seq->options;
+//	stmt->options = list_make1(defWithOids(false));
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = false;
@@ -227,6 +235,14 @@ DefineSequence(CreateSeqStmt *seq)
 	if (owned_by)
 		process_owned_by(rel, owned_by);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	heap_close(rel, NoLock);
 
 	return seqoid;
@@ -271,6 +287,11 @@ ResetSequence(Oid seq_relid)
 	UnlockReleaseBuffer(buf);
 
 	/*
+	 * Check whether we can reset the sequence value with lock held.
+	 */
+	sequence_setval(seq_rel, seq->start_value, true);
+
+	/*
 	 * Modify the copied tuple to execute the restart (compare the RESTART
 	 * action in AlterSequence)
 	 */
@@ -406,6 +427,7 @@ Oid
 AlterSequence(AlterSeqStmt *stmt)
 {
 	Oid			relid;
+	Oid			seqamid;
 	SeqTable	elm;
 	Relation	seqrel;
 	Buffer		buf;
@@ -434,11 +456,17 @@ AlterSequence(AlterSeqStmt *stmt)
 	/* lock page' buffer and read tuple into new sequence structure */
 	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
 
-	/* Copy old values of options into workspace */
+	/* Copy old values of params into workspace */
 	memcpy(&new, seq, sizeof(FormData_pg_sequence));
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->params, false, &new, &owned_by);
+	seqamid = init_options(stmt->accessMethod, stmt->options);
+
+	/*
+	 * Check whether we can reset the sequence value with lock held.
+	 */
+	sequence_setval(seqrel, new.last_value, true);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
@@ -484,6 +512,11 @@ AlterSequence(AlterSeqStmt *stmt)
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
+	/*
+	 * Change the SeqAm, if requested, using a transactional update.
+	 */
+	seqrel_update_relam(relid, seqamid);
+
 	relation_close(seqrel, NoLock);
 
 	return relid;
@@ -872,6 +905,11 @@ do_setval(Oid relid, int64 next, bool iscalled)
 						bufm, bufx)));
 	}
 
+	/*
+	 * Check whether we can reset the sequence value without lock held.
+	 */
+	sequence_setval(seqrel, next, false);
+
 	/* Set the currval() state only if iscalled = true */
 	if (iscalled)
 	{
@@ -1127,15 +1165,15 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1145,13 +1183,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1396,7 +1434,7 @@ init_params(List *options, bool isInit,
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1544,6 +1582,39 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+	}
+
+	heap_close(rd, RowExclusiveLock);
+}
 
 void
 seq_redo(XLogRecPtr lsn, XLogRecord *record)
@@ -1599,3 +1670,34 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record)
 
 	pfree(localpage);
 }
+
+static Oid
+init_options(char *accessMethod, List *options)
+{
+	Oid         seqamid;
+	Datum       reloptions;
+	Form_pg_seqam seqamForm;
+	HeapTuple   tuple;
+
+     /*
+	  *  Parse AM-specific options, convert to text array form,
+	  *  retrieve the AM-option function and then validate.
+	  */
+	reloptions = transformRelOptions((Datum) 0, options,
+											NULL, NULL, false, false);
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(accessMethod));
+	if (!HeapTupleIsValid(tuple))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("access method \"%s\" does not exist",
+	                      accessMethod)));
+	seqamid = HeapTupleGetOid(tuple);
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	(void) sequence_reloptions(seqamForm->seqamoptions, reloptions, true);
+
+	ReleaseSysCache(tuple);
+
+	return seqamid;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cad8311..6977949 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8529,6 +8529,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 		case RELKIND_INDEX:
 			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
+		case RELKIND_SEQUENCE:
+			(void) sequence_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 51fdb63..6598f76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3237,8 +3237,10 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 	CreateSeqStmt *newnode = makeNode(CreateSeqStmt);
 
 	COPY_NODE_FIELD(sequence);
+	COPY_NODE_FIELD(params);
 	COPY_NODE_FIELD(options);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
@@ -3249,8 +3251,10 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 	AlterSeqStmt *newnode = makeNode(AlterSeqStmt);
 
 	COPY_NODE_FIELD(sequence);
+	COPY_NODE_FIELD(params);
 	COPY_NODE_FIELD(options);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4b219b3..8fbdc64 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1532,8 +1532,10 @@ static bool
 _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
+	COMPARE_NODE_FIELD(params);
 	COMPARE_NODE_FIELD(options);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
@@ -1542,8 +1544,10 @@ static bool
 _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
+	COMPARE_NODE_FIELD(params);
 	COMPARE_NODE_FIELD(options);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 76ef11e..f363fcb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -53,6 +53,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3195,7 +3196,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
-					n->options = $5;
+					n->params = $5;
+					n->accessMethod = DEFAULT_SEQAM;
+					n->options = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->params = $5;
+					n->accessMethod = $7;
+					n->options = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->params = $5;
+					n->accessMethod = $7;
+					n->options = $9;
 					n->ownerId = InvalidOid;
 					$$ = (Node *)n;
 				}
@@ -3206,7 +3233,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
-					n->options = $4;
+					n->params = $4;
+					n->accessMethod = DEFAULT_SEQAM;
+					n->options = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name SeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->params = $4;
+					n->accessMethod = $6;
+					n->options = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name SeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->params = $4;
+					n->accessMethod = $6;
+					n->options = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3214,11 +3265,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
-					n->options = $6;
+					n->params = $6;
+					n->accessMethod = DEFAULT_SEQAM;
+					n->options = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name SeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->params = $6;
+					n->accessMethod = $8;
+					n->options = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name SeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->params = $6;
+					n->accessMethod = $8;
+					n->options = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3277,7 +3351,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 10a3be5..e642832 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -391,7 +391,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		 */
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
-		seqstmt->options = NIL;
+		seqstmt->params = NIL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
@@ -417,7 +417,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		attnamelist = list_make3(makeString(snamespace),
 								 makeString(cxt->relation->relname),
 								 makeString(column->colname));
-		altseqstmt->options = list_make1(makeDefElem("owned_by",
+		altseqstmt->params = list_make1(makeDefElem("owned_by",
 													 (Node *) attnamelist));
 
 		cxt->alist = lappend(cxt->alist, altseqstmt);
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 25f50e5..c4af2d7 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1005,10 +1005,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index bfc3c86..03a9db9 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -47,6 +47,7 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_range.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -624,6 +625,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		1024
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAmNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_amname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAmOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index ac5e4f3..60a8d8f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -120,6 +120,7 @@
 extern bool Log_disconnections;
 extern int	CommitDelay;
 extern int	CommitSiblings;
+extern char *default_seq_mgr;
 extern char *default_tablespace;
 extern char *temp_tablespaces;
 extern bool synchronize_seqscans;
@@ -2597,6 +2598,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"default_sequence_mgr", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence manager for any new sequences."),
+			gettext_noop("An empty string selects the 'local' sequence manager."),
+			GUC_IS_NAME | GUC_NOT_IN_SAMPLE
+		},
+		&default_seq_mgr,
+		"",
+		check_default_seq_mgr, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 5a4664b..ef523b7 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -45,8 +45,10 @@ typedef enum relopt_kind
 	RELOPT_KIND_TABLESPACE = (1 << 7),
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
+	RELOPT_KIND_SEQUENCE = (1 << 10),
+
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_VIEW,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -270,6 +272,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
 				 bool validate);
+extern bytea *sequence_reloptions(RegProcedure amoptions, Datum reloptions,
+				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..750cdc2
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "access/genam.h"
+#include "utils/relcache.h"
+
+extern void sequence_alloc(Relation seqRelation, int64 start, int64 end);
+extern void sequence_setval(Relation seqRelation, int64 next, bool isLocked);
+
+extern void seqam_local_alloc(void);
+extern bool seqam_local_setval(Relation seqRelation, int64 next, bool isLocked);
+extern Datum seqam_local_options(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 6251fb8..82eaaf8 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -211,6 +211,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 3480, on pg_seqam using btree(seqamname name_ops));
+#define SeqAmNameIndexId  3480
+DECLARE_UNIQUE_INDEX(pgseq_am_oid_index, 3481, on pg_seqam using btree(oid oid_ops));
+#define SeqAmOidIndexId  3481
+
 /* This following index is not used for a cache and is not unique */
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 010605d..4048c7d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4677,6 +4677,12 @@ DESCR("SP-GiST support for quad tree over range");
 DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_  spg_range_quad_leaf_consistent _null_ _null_ _null_ ));
 DESCR("SP-GiST support for quad tree over range");
 
+DATA(insert OID = 3480 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 3481 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Local SequenceAM setval");
+DATA(insert OID = 3482 (  seqam_local_options	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ seqam_local_options _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
 
 /*
  * Symbolic values for provolatile column: these indicate whether the result
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..44f1c58
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,70 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	3819
+
+CATALOG(pg_seqam,3819)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamoptions;		/* parse AM-specific parameters */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						4
+#define Anum_pg_seqam_amname				1
+#define Anum_pg_seqam_amalloc				2
+#define Anum_pg_seqam_amsetval				3
+#define Anum_pg_seqam_amoptions				4
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 3839 (  local		seqam_local_alloc seqam_local_setval seqam_local_options));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 3839
+
+#define DEFAULT_SEQAM	""
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 56cf592..e8d43ee 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1833,16 +1833,20 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List	   *params;			/* Pre-9.3, fixed params were called options */
+	List       *options;        /* WITH clause options: a list of DefElem */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } CreateSeqStmt;
 
 typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List	   *params;			/* Pre-9.3, fixed params were called options */
+	List       *options;        /* WITH clause options: a list of DefElem */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 0023c00..e6bcfbf 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -371,6 +371,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_default_seq_mgr(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index bde5f17..c208dfe 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -48,10 +48,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the index access method functions defined
- * by the pg_am row associated with an index relation.
+ * by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+	/* pg_am only */
 	FmgrInfo	aminsert;
 	FmgrInfo	ambeginscan;
 	FmgrInfo	amgettuple;
@@ -66,6 +68,12 @@ typedef struct RelationAmInfo
 	FmgrInfo	amvacuumcleanup;
 	FmgrInfo	amcanreturn;
 	FmgrInfo	amcostestimate;
+
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+
+	/* Common */
 	FmgrInfo	amoptions;
 } RelationAmInfo;
 
@@ -134,22 +142,22 @@ typedef struct RelationData
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * Access support info (used only for index or sequence relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
 	 * arrays are indexed by support function number, which is a sufficient
 	 * identifier given that restriction.
 	 *
-	 * Note: rd_amcache is available for index AMs to cache private data about
-	 * an index.  This must be just a cache since it may get reset at any time
+	 * Note: rd_amcache is available for AMs to cache private data about
+	 * an object.  This must be just a cache since it may get reset at any time
 	 * (in particular, it will get reset by a relcache inval message for the
-	 * index).	If used, it must point to a single memory chunk palloc'd in
+	 * object).	If used, it must point to a single memory chunk palloc'd in
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
-	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am */
+	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am or pg_seqam */
 	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
@@ -160,7 +168,7 @@ typedef struct RelationData
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
-	void	   *rd_amcache;		/* available for use by index AM */
+	void	   *rd_amcache;		/* available for use by AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
 
 	/*
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index d1d8abe..d47aec1 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
#2Simon Riggs
simon@2ndQuadrant.com
In reply to: Simon Riggs (#1)
3 attachment(s)
Re: Sequence Access Method WIP

On 16 January 2013 00:40, Simon Riggs <simon@2ndquadrant.com> wrote:

SeqAm allows you to specify a plugin that alters the behaviour for
sequence allocation and resetting, aimed specifically at clustering
systems.

New command options on end of statement allow syntax
CREATE SEQUENCE foo_seq
USING globalseq

Production version of this, ready for commit to PG 9.4

Includes test extension which allows sequences without gaps - "gapless".

Test using seq_test.psql after creating extension.

No dependencies on other patches.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

seqam.v33.patchapplication/octet-stream; name=seqam.v33.patchDownload
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index c32088f..aea4a14 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,6 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = common gin gist hash heap index nbtree rmgrdesc spgist transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index b5fd30a..950c291 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -274,6 +274,7 @@ static bool need_initialization = true;
 static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 					int text_len, bool validate);
+static bytea *common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate);
 
 /*
  * initialize_reloptions
@@ -809,6 +810,9 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 		case RELKIND_INDEX:
 			options = index_reloptions(amoptions, datum, false);
 			break;
+		case RELKIND_SEQUENCE:
+			options = sequence_reloptions(amoptions, datum, false);
+			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
 			break;
@@ -1219,13 +1223,31 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 /*
  * Parse options for indexes.
+ */
+bytea *
+index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for sequences.
+ */
+bytea *
+sequence_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
-bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+static bytea *
+common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..f782f7e
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..c0106c6
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,253 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAm. That is also the basic default.  Local
+ * SeqAm assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAm mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAm would also wish to control node-level caches for sequences to
+ * ensure good performance.  The CACHE option and other options may be
+ * overridden by the _alloc API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAm allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module.  The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, am_data.
+ *
+ * We currently assume that there is no persistent state held within the SeqAm,
+ * so it is safe to ALTER the access method of an object without taking any
+ * special actions, as long as we hold an AccessExclusiveLock while we do that.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAm data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*default_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname) \
+do { \
+	procedure = &seqRelation->rd_aminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqRelation->rd_seqam->pname; \
+		if (!RegProcedureIsValid(procOid)) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+		fmgr_info_cxt(procOid, procedure, seqRelation->rd_indexcxt); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Manager API
+ *
+ *  INTERFACE ROUTINES
+ *		sequence_alloc		- allocate a new range of values for the sequence
+ *		sequence_setval		- coordinate the reset of a sequence to new value
+ *
+ *		sequence_reloptions	- process options - located in reloptions.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * sequence_alloc - allocate sequence values in a sequence
+ */
+void
+sequence_alloc(Relation seqRelation, SeqTable seq_elem, Buffer buf, HeapTuple tup)
+{
+	FmgrInfo   *procedure;
+
+	Assert(RelationIsValid(seqRelation));
+	Assert(PointerIsValid(seqRelation->rd_seqam));
+	Assert(OidIsValid(seqRelation->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc);
+
+	/*
+	 * have the seqam's alloc proc do all the work.
+	 */
+	FunctionCall4(procedure,
+				  PointerGetDatum(seqRelation),
+				  PointerGetDatum(seq_elem),
+				  Int32GetDatum(buf),
+				  PointerGetDatum(tup));
+}
+
+/*
+ * sequence_setval - set sequence values in a sequence
+ */
+void
+sequence_setval(Relation seqRelation, SeqTable seq_elem, Buffer buf, HeapTuple tup, int64 next, bool iscalled)
+{
+	FmgrInfo   *procedure;
+
+	Assert(RelationIsValid(seqRelation));
+	Assert(PointerIsValid(seqRelation->rd_seqam));
+	Assert(OidIsValid(seqRelation->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval);
+
+	/*
+	 * have the seqam's setval proc do all the work.
+	 */
+	FunctionCall6(procedure,
+				  PointerGetDatum(seqRelation),
+				  PointerGetDatum(seq_elem),
+				  Int32GetDatum(buf),
+				  PointerGetDatum(tup),
+				  Int64GetDatum(next),
+				  BoolGetDatum(iscalled));
+}
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new default_sequenceam */
+bool
+check_default_seqam(char **newval, void **extra, GucSource source)
+{
+	if (**newval == '\0')
+		return true;
+
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.	Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence manager \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence manager \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+/*
+ * GetDefaultSeqAM -- get the OID of the current default sequence AM
+ *
+ * This exists to hide (and possibly optimize the use of) the
+ * default_seqam GUC variable.
+ */
+Oid
+GetDefaultSeqAM(void)
+{
+	/* Fast path for default_tablespace == "" */
+	if (default_seqam == NULL || default_seqam[0] == '\0')
+		return LOCAL_SEQAM_OID;
+
+	return get_seqam_oid(default_seqam, false);
+}
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+	if (!HeapTupleIsValid(tuple))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("squence am \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a974bd5..c85b5d1 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index f3344c6..5ca29a1 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,13 +14,19 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -55,32 +61,6 @@ typedef struct sequence_magic
 	uint32		magic;
 } sequence_magic;
 
-/*
- * We store a SeqTable item for every sequence we have touched in the current
- * session.  This is needed to hold onto nextval/currval state.  (We can't
- * rely on the relcache, since it's only, well, a cache, and may decide to
- * discard entries.)
- *
- * XXX We use linear search to find pre-existing SeqTable entries.	This is
- * good when only a small number of sequences are touched in a session, but
- * would suck with many different sequences.  Perhaps use a hashtable someday.
- */
-typedef struct SeqTableData
-{
-	struct SeqTableData *next;	/* link to next SeqTable object */
-	Oid			relid;			/* pg_class OID of this sequence */
-	Oid			filenode;		/* last seen relfilenode of this sequence */
-	LocalTransactionId lxid;	/* xact in which we last did a seq op */
-	bool		last_valid;		/* do we have a valid "last" value? */
-	int64		last;			/* value last returned by nextval */
-	int64		cached;			/* last value already cached for nextval */
-	/* if last != cached, we have not used up all the cached values */
-	int64		increment;		/* copy of sequence's increment field */
-	/* note that increment is zero until we first do read_seq_tuple() */
-} SeqTableData;
-
-typedef SeqTableData *SeqTable;
-
 static SeqTable seqtab = NULL;	/* Head of list of SeqTable items */
 
 /*
@@ -92,13 +72,12 @@ static SeqTableData *last_used_seq = NULL;
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
-static void init_params(List *options, bool isInit,
+static void init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by);
 static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static Oid init_options(char *accessMethod, List *options);
 
 
 /*
@@ -112,6 +91,7 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	Relation	rel;
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
@@ -119,6 +99,9 @@ DefineSequence(CreateSeqStmt *seq)
 	bool		null[SEQ_COL_LASTCOL];
 	int			i;
 	NameData	name;
+	HeapTupleData seqtuple;
+	SeqTable	elm;
+	Buffer		buf;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -126,8 +109,9 @@ DefineSequence(CreateSeqStmt *seq)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("unlogged sequences are not supported")));
 
-	/* Check and set all option values */
+	/* Check and set all param values */
 	init_params(seq->options, true, &new, &owned_by);
+	seqamid = init_options(seq->accessMethod, seq->amoptions);
 
 	/*
 	 * Create relation (and fill value[] and null[] for the tuple)
@@ -203,6 +187,11 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "is_called";
 				value[i - 1] = BoolGetDatum(false);
 				break;
+			case SEQ_COL_AMDATA:
+				coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+				coldef->colname = "amdata";
+				null[i - 1] = true;
+				break;
 		}
 		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
@@ -218,6 +207,14 @@ DefineSequence(CreateSeqStmt *seq)
 	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
 	Assert(seqoid != InvalidOid);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	rel = heap_open(seqoid, AccessExclusiveLock);
 	tupDesc = RelationGetDescr(rel);
 
@@ -231,6 +228,19 @@ DefineSequence(CreateSeqStmt *seq)
 
 	heap_close(rel, NoLock);
 
+	/*
+	 * reopen relation, read sequence for the benfit of the AM. It would be
+	 * nice to do this with less repetitive work.
+	 */
+	init_sequence(seqoid, &elm, &rel);
+	(void) read_seq_tuple(elm, rel, &buf, &seqtuple);
+
+	/* call into AM */
+	sequence_setval(rel, elm, buf, &seqtuple, new.start_value, false);
+
+	UnlockReleaseBuffer(buf);
+	heap_close(rel, NoLock);
+
 	return seqoid;
 }
 
@@ -273,15 +283,6 @@ ResetSequence(Oid seq_relid)
 	UnlockReleaseBuffer(buf);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
-	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
-
-	/*
 	 * Create a new storage file for the sequence.	We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
@@ -290,14 +291,25 @@ ResetSequence(Oid seq_relid)
 							  InvalidMultiXactId);
 
 	/*
-	 * Insert the modified tuple into the new storage file.
+	 * Insert the modified tuple into the new storage file. This will log
+	 * superflously log the old values, but this isn't a performance critical
+	 * path...
 	 */
 	fill_seq_with_data(seq_rel, tuple);
 
+	/* read from the new relfilenode, pointing into buffer again */
+	seq = read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+
+	/* call into the sequence am, let it do whatever it wants */
+	sequence_setval(seq_rel, elm, buf, &seqtuple, seq->start_value, false);
+
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
 	elm->cached = elm->last;
 
+	/* And now done with the old page */
+	UnlockReleaseBuffer(buf);
+
 	relation_close(seq_rel, NoLock);
 }
 
@@ -344,6 +356,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	 * the right xmin, and (b) REDO of the heap_insert record would re-init
 	 * page and sequence magic number would be lost.  This means two log
 	 * records instead of one :-(
+	 * XXX: Why don't we just pass HEAP_INSERT_SKIP_WAL to heap_insert?
 	 */
 	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
@@ -373,27 +386,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	MarkBufferDirty(buf);
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		XLogRecData rdata[2];
-
-		xlrec.node = rel->rd_node;
-		rdata[0].data = (char *) &xlrec;
-		rdata[0].len = sizeof(xl_seq_rec);
-		rdata[0].buffer = InvalidBuffer;
-		rdata[0].next = &(rdata[1]);
-
-		rdata[1].data = (char *) tuple->t_data;
-		rdata[1].len = tuple->t_len;
-		rdata[1].buffer = InvalidBuffer;
-		rdata[1].next = NULL;
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, page);
 
 	END_CRIT_SECTION();
 
@@ -409,12 +402,14 @@ Oid
 AlterSequence(AlterSeqStmt *stmt)
 {
 	Oid			relid;
+	Oid			seqamid;
 	SeqTable	elm;
 	Relation	seqrel;
 	Buffer		buf;
 	HeapTupleData seqtuple;
+	HeapTuple newtuple;
 	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
 
 	/* Open and lock sequence. */
@@ -437,11 +432,16 @@ AlterSequence(AlterSeqStmt *stmt)
 	/* lock page' buffer and read tuple into new sequence structure */
 	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
 
-	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	/*
+	 * copy tuple so init_params can scribble on it and error out later without
+	 * causing problems.
+	 */
+	newtuple = heap_copytuple(&seqtuple);
+	new = (Form_pg_sequence) GETSTRUCT(newtuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
+	seqamid = init_options(stmt->accessMethod, stmt->amoptions);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
@@ -450,33 +450,15 @@ AlterSequence(AlterSeqStmt *stmt)
 	/* Now okay to update the on-disk tuple */
 	START_CRIT_SECTION();
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+	/* we copy back the tuple, contains new settings but is not yet reset via AM*/
+	Assert(newtuple->t_len == seqtuple.t_len);
+	memcpy(seqtuple.t_data, newtuple->t_data, newtuple->t_len);
 
 	MarkBufferDirty(buf);
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		XLogRecData rdata[2];
-		Page		page = BufferGetPage(buf);
-
-		xlrec.node = seqrel->rd_node;
-		rdata[0].data = (char *) &xlrec;
-		rdata[0].len = sizeof(xl_seq_rec);
-		rdata[0].buffer = InvalidBuffer;
-		rdata[0].next = &(rdata[1]);
-
-		rdata[1].data = (char *) seqtuple.t_data;
-		rdata[1].len = seqtuple.t_len;
-		rdata[1].buffer = InvalidBuffer;
-		rdata[1].next = NULL;
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
-
-		PageSetLSN(page, recptr);
-	}
+	/* call SeqAM, in a crit section since we already modified the tuple */
+	sequence_setval(seqrel, elm, buf, &seqtuple,
+					seq->last_value, seq->is_called);
 
 	END_CRIT_SECTION();
 
@@ -486,6 +468,11 @@ AlterSequence(AlterSeqStmt *stmt)
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
+	/*
+	 * Change the SeqAm, if requested, using a transactional update.
+	 */
+	seqrel_update_relam(relid, seqamid);
+
 	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
 
 	relation_close(seqrel, NoLock);
@@ -529,26 +516,20 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
 	Buffer		buf;
-	Page		page;
 	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	int64		result;
+
+	/* common code path for all sequence AMs */
 
 	/* open and AccessShareLock sequence */
 	init_sequence(relid, &elm, &seqrel);
@@ -564,7 +545,8 @@ nextval_internal(Oid relid)
 	if (!seqrel->rd_islocaltemp)
 		PreventCommandIfReadOnly("nextval()");
 
-	if (elm->last != elm->cached)		/* some numbers were cached */
+	/* return cached values without entering the sequence am again */
+	if (elm->last != elm->cached)
 	{
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
@@ -575,178 +557,14 @@ nextval_internal(Oid relid)
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.	If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+	read_seq_tuple(elm, seqrel, &buf, &seqtuple);
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
-
-	/* save info in local cache */
-	elm->last = result;			/* last returned number */
-	elm->cached = last;			/* last fetched number */
-	elm->last_valid = true;
+	/* call into AM specific code */
+	sequence_alloc(seqrel, elm, buf, &seqtuple);
 
 	last_used_seq = elm;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.	Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		XLogRecData rdata[2];
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-		rdata[0].data = (char *) &xlrec;
-		rdata[0].len = sizeof(xl_seq_rec);
-		rdata[0].buffer = InvalidBuffer;
-		rdata[0].next = &(rdata[1]);
-
-		rdata[1].data = (char *) seqtuple.t_data;
-		rdata[1].len = seqtuple.t_len;
-		rdata[1].buffer = InvalidBuffer;
-		rdata[1].next = NULL;
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
+	result = elm->last;
 
 	UnlockReleaseBuffer(buf);
 
@@ -875,51 +693,22 @@ do_setval(Oid relid, int64 next, bool iscalled)
 						bufm, bufx)));
 	}
 
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
-
 	/* In any case, forget any future cached numbers */
 	elm->cached = elm->last;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
+	sequence_setval(seqrel, elm, buf, &seqtuple, next, iscalled);
 
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
+	Assert(seq->is_called == iscalled);
 
-	MarkBufferDirty(buf);
+	/* common logic we don't have to duplicate in every AM implementation */
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
 	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		XLogRecData rdata[2];
-		Page		page = BufferGetPage(buf);
-
-		xlrec.node = seqrel->rd_node;
-		rdata[0].data = (char *) &xlrec;
-		rdata[0].len = sizeof(xl_seq_rec);
-		rdata[0].buffer = InvalidBuffer;
-		rdata[0].next = &(rdata[1]);
-
-		rdata[1].data = (char *) seqtuple.t_data;
-		rdata[1].len = seqtuple.t_len;
-		rdata[1].buffer = InvalidBuffer;
-		rdata[1].next = NULL;
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
-
-		PageSetLSN(page, recptr);
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
 	}
 
-	END_CRIT_SECTION();
-
 	UnlockReleaseBuffer(buf);
 
 	relation_close(seqrel, NoLock);
@@ -1002,7 +791,7 @@ open_share_lock(SeqTable seq)
  * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
  * output parameters.
  */
-static void
+void
 init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 {
 	SeqTable	elm;
@@ -1039,6 +828,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 		elm->last_valid = false;
 		elm->last = elm->cached = elm->increment = 0;
 		elm->next = seqtab;
+		elm->am_private = PointerGetDatum(NULL);
 		seqtab = elm;
 	}
 
@@ -1079,7 +869,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
  *
  * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
+Form_pg_sequence
 read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 {
 	Page		page;
@@ -1130,15 +920,15 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1148,13 +938,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1399,7 +1189,7 @@ init_params(List *options, bool isInit,
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1415,8 +1205,7 @@ process_owned_by(Relation seqrel, List *owned_by)
 
 	nnames = list_length(owned_by);
 	Assert(nnames > 0);
-	if (nnames == 1)
-	{
+	if (nnames == 1)	{
 		/* Must be OWNED BY NONE */
 		if (strcmp(strVal(linitial(owned_by)), "none") != 0)
 			ereport(ERROR,
@@ -1548,6 +1337,42 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+		CatalogUpdateIndexes(rd, ctup);
+	}
+
+	heap_freetuple(ctup);
+	heap_close(rd, RowExclusiveLock);
+	CommandCounterIncrement();
+}
 
 void
 seq_redo(XLogRecPtr lsn, XLogRecord *record)
@@ -1603,6 +1428,7 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record)
 	pfree(localpage);
 }
 
+
 /*
  * Flush cached sequence information.
  */
@@ -1620,3 +1446,279 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+static Oid
+init_options(char *accessMethod, List *options)
+{
+	Oid         seqamid;
+	Datum       reloptions;
+	Form_pg_seqam seqamForm;
+	HeapTuple   tuple = NULL;
+	char	   *validnsps[] = {NULL, NULL};
+
+	if (accessMethod == NULL || strcmp(accessMethod, DEFAULT_SEQAM) == 0)
+		seqamid = GetDefaultSeqAM();
+	else
+		seqamid = get_seqam_oid(accessMethod, false);
+
+	tuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(seqamid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	/* allow am specific options */
+	validnsps[0] = NameStr(seqamForm->seqamname);
+
+     /*
+	  *  Parse AM-specific options, convert to text array form,
+	  *  retrieve the AM-option function and then validate.
+	  */
+	reloptions = transformRelOptions((Datum) NULL, options,
+									 NULL, validnsps, false, false);
+
+	(void) sequence_reloptions(seqamForm->seqamoptions, reloptions, true);
+
+	ReleaseSysCache(tuple);
+
+	return seqamid;
+}
+
+void
+log_sequence_tuple(Relation seqrel, HeapTuple tup, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+	XLogRecData rdata[2];
+
+	xlrec.node = seqrel->rd_node;
+	rdata[0].data = (char *) &xlrec;
+	rdata[0].len = sizeof(xl_seq_rec);
+	rdata[0].buffer = InvalidBuffer;
+	rdata[0].next = &(rdata[1]);
+
+	rdata[1].data = (char *) tup->t_data;
+	rdata[1].len = tup->t_len;
+	rdata[1].buffer = InvalidBuffer;
+	rdata[1].next = NULL;
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
+
+	PageSetLSN(page, recptr);
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = LOCAL functions
+ *
+ *------------------------------------------------------------
+ */
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+void
+sequence_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SeqTable	elm = (SeqTable) PG_GETARG_POINTER(1);
+	Buffer		buf = (Buffer) PG_GETARG_INT32(2);
+	HeapTuple	seqtuple = (HeapTuple) PG_GETARG_POINTER(3);
+	Page		page;
+	Form_pg_sequence seq;
+	bool		logit = false;
+	int64		incby,
+				maxv,
+				minv,
+				cache,
+				log,
+				fetch,
+				last;
+	int64		result,
+				next;
+	int64		rescnt = 0; /* how many values did we fetch up to now */
+
+	page = BufferGetPage(buf);
+	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+
+	last = next = result = seq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	fetch = cache = seq->cache_value;
+	log = seq->log_cnt;
+
+	if (!seq->is_called)
+	{
+		rescnt++;			   /* return current last_value if not is_called */
+		fetch--;
+		logit = true;
+	}
+
+	/* check whether value can be satisfied without logging again */
+	if (log < fetch || !seq->is_called || PageGetLSN(page) <= GetRedoRecPtr())
+	{
+		/* forced log to satisfy local demand for values */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	while (fetch)				/* try to fetch cache [+ log ] numbers */
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/* it's ok, only additional cached values exceed maximum */
+				if (rescnt > 0)
+					break;
+				if (!seq->is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				if (rescnt > 0)
+					break;		/* stop fetching */
+				if (!seq->is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+
+		fetch--;
+		if (rescnt < cache)
+		{
+			log--;
+			rescnt++;
+			last = next;
+			if (rescnt == 1)	/* if it's first result - */
+				result = next;	/* it's what to return */
+		}
+	}
+
+	elm->last = result;
+	elm->cached = last;
+	elm->last_valid = true;
+
+	/* ready to change the on-disk (or really, in-buffer) tuple */
+	START_CRIT_SECTION();
+
+	/*
+	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
+	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
+	 * This looks like a violation of the buffer update protocol, but it is
+	 * in fact safe because we hold exclusive lock on the buffer.  Any other
+	 * process, including a checkpoint, that tries to examine the buffer
+	 * contents will block until we release the lock, and then will see the
+	 * final state that we install below.
+	 */
+	MarkBufferDirty(buf);
+
+	if (logit)
+	{
+		/*
+		 * We don't log the current state of the tuple, but rather the state
+		 * as it would appear after "log" more fetches.  This lets us skip
+		 * that many future WAL records, at the cost that we lose those
+		 * sequence values if we crash.
+		 */
+
+		seq->last_value = next;
+		seq->is_called = true;
+		seq->log_cnt = 0;
+		log_sequence_tuple(seqrel, seqtuple, page);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = elm->last; /* last fetched number */
+	seq->is_called = true;
+	seq->log_cnt = log; /* how much is logged */
+
+	result = elm->last;
+
+	END_CRIT_SECTION();
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Coordinate the setting of a local sequence.
+ */
+void
+sequence_local_setval(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	Buffer		buf = (Buffer) PG_GETARG_INT32(2);
+	HeapTuple	seqtuple = (HeapTuple) PG_GETARG_POINTER(3);
+	int64		next = PG_GETARG_INT64(4);
+	bool		iscalled = PG_GETARG_BOOL(5);
+	Page		page = BufferGetPage(buf);
+	Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+
+	/* ready to change the on-disk (or really, in-buffer) tuple */
+	START_CRIT_SECTION();
+
+	/* set is_called, all AMs should need to do this */
+	seq->is_called = iscalled;
+	seq->last_value = next;		/* last fetched number */
+	seq->log_cnt = 0;
+
+	MarkBufferDirty(buf);
+
+	log_sequence_tuple(seqrel, seqtuple, page);
+
+	END_CRIT_SECTION();
+}
+
+/*
+ * seqam_local_options()
+ *
+ * Verify the options of a local sequence.
+ */
+Datum
+sequence_local_options(PG_FUNCTION_ARGS)
+{
+	Datum       reloptions = PG_GETARG_DATUM(0);
+	bool        validate = PG_GETARG_BOOL(1);
+	bytea      *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+	PG_RETURN_NULL();
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 670af18..4a6e0f2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8772,6 +8772,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 		case RELKIND_INDEX:
 			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
+		case RELKIND_SEQUENCE:
+			(void) sequence_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 65f3b98..fc309e2 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3277,7 +3277,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
@@ -3289,7 +3291,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4c9b05e..258c09e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1542,7 +1542,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
@@ -1552,7 +1554,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 363c603..d4511e5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3331,7 +3332,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = DEFAULT_SEQAM;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					$$ = (Node *)n;
 				}
@@ -3342,7 +3369,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = DEFAULT_SEQAM;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3350,11 +3401,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = DEFAULT_SEQAM;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3413,7 +3487,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 19d19e5f..a16c9bb 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -399,6 +399,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index c467f11..1f123f2 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1043,10 +1043,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b4cc6ad..ea5eb1a 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -50,6 +50,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -241,6 +242,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -932,11 +934,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->trigdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1370,6 +1375,39 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for an index relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	MemoryContext	indexcxt;
+	Form_pg_seqam	amform;
+
+	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+									 RelationGetRelationName(rel),
+									 ALLOCSET_SMALL_MINSIZE,
+									 ALLOCSET_SMALL_INITSIZE,
+									 ALLOCSET_SMALL_MAXSIZE);
+	rel->rd_indexcxt = indexcxt;
+
+	rel->rd_aminfo = (RelationAmInfo *)
+		MemoryContextAllocZero(rel->rd_indexcxt,
+							   sizeof(RelationAmInfo));
+
+	/*
+	 * Make a copy of the pg_am entry for the index's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+}
 
 /*
  *		formrdesc
@@ -4375,6 +4413,22 @@ load_relcache_init_file(bool shared)
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
+		else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+		{
+			MemoryContext indexcxt;
+			Assert(!rel->rd_isnailed);
+			Assert(false);
+
+			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+											 RelationGetRelationName(rel),
+											 ALLOCSET_SMALL_MINSIZE,
+											 ALLOCSET_SMALL_INITSIZE,
+											 ALLOCSET_SMALL_MAXSIZE);
+			rel->rd_indexcxt = indexcxt;
+			/* set up zeroed fmgr-info vectors */
+			rel->rd_aminfo = (RelationAmInfo *)
+				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+		}
 		else
 		{
 			/* Count nailed rels to ensure we have 'em all */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index e9bdfea..2fddb27 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAmNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_amname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAmOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 538d027..a3e3d0d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2663,6 +2664,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"default_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence am for any new sequences."),
+			gettext_noop("An empty string selects the 'local' sequence am."),
+			GUC_IS_NAME | GUC_NOT_IN_SAMPLE
+		},
+		&default_seqam,
+		"",
+		check_default_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 5a4664b..ef523b7 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -45,8 +45,10 @@ typedef enum relopt_kind
 	RELOPT_KIND_TABLESPACE = (1 << 7),
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
+	RELOPT_KIND_SEQUENCE = (1 << 10),
+
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_VIEW,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -270,6 +272,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
 				 bool validate);
+extern bytea *sequence_reloptions(RegProcedure amoptions, Datum reloptions,
+				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..61a1d1b
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,70 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+/*
+ * We store a SeqTable item for every sequence we have touched in the current
+ * session.  This is needed to hold onto nextval/currval state.  (We can't
+ * rely on the relcache, since it's only, well, a cache, and may decide to
+ * discard entries.)
+ *
+ * XXX We use linear search to find pre-existing SeqTable entries.	This is
+ * good when only a small number of sequences are touched in a session, but
+ * would suck with many different sequences.  Perhaps use a hashtable someday.
+ */
+typedef struct SeqTableData
+{
+	struct SeqTableData *next;	/* link to next SeqTable object */
+	Oid			relid;			/* pg_class OID of this sequence */
+	Oid			filenode;		/* last seen relfilenode of this sequence */
+	LocalTransactionId lxid;	/* xact in which we last did a seq op */
+	bool		last_valid;		/* do we have a valid "last" value? */
+	int64		last;			/* value last returned by nextval */
+	int64		cached;			/* last value already cached for nextval */
+	/* if last != cached, we have not used up all the cached values */
+	int64		increment;		/* copy of sequence's increment field */
+	/* note that increment is zero until we first do read_seq_tuple() */
+	Datum		am_private;		/* private data of the SeqAm */
+} SeqTableData;
+
+typedef SeqTableData *SeqTable;
+
+extern char *default_seqam;
+
+Oid GetDefaultSeqAM(void);
+
+extern void sequence_alloc(Relation seqRelation, SeqTable seq_elem, Buffer buf, HeapTuple tup);
+extern void sequence_setval(Relation seqRelation, SeqTable seq_elem, Buffer buf, HeapTuple tup, int64 next, bool iscalled);
+
+
+extern void sequence_local_alloc(PG_FUNCTION_ARGS);
+extern void sequence_local_setval(PG_FUNCTION_ARGS);
+extern Datum sequence_local_options(PG_FUNCTION_ARGS);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void log_sequence_tuple(Relation seqrel, HeapTuple tup, Page page);
+extern void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
+extern Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
+			   Buffer *buf, HeapTuple seqtuple);
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 4860e98..bb3c6e6 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -213,6 +213,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 3480, on pg_seqam using btree(seqamname name_ops));
+#define SeqAmNameIndexId  3480
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 3481, on pg_seqam using btree(oid oid_ops));
+#define SeqAmOidIndexId  3481
+
 /* This following index is not used for a cache and is not unique */
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ca4fc62..54d4e13 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4746,6 +4746,12 @@ DESCR("SP-GiST support for quad tree over range");
 DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_  spg_range_quad_leaf_consistent _null_ _null_ _null_ ));
 DESCR("SP-GiST support for quad tree over range");
 
+DATA(insert OID = 3939 (  sequence_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ sequence_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 3940 (  sequence_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 2281 "2281 2281 2281 20 16" _null_ _null_ _null_ _null_ sequence_local_setval _null_ _null_ _null_ ));
+DESCR("Local SequenceAM setval");
+DATA(insert OID = 3936 (  sequence_local_options	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 2281 "2281 16" _null_ _null_ _null_ _null_ sequence_local_options _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
 
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..00a6f03
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,70 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	32
+
+CATALOG(pg_seqam,32)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamoptions;		/* parse AM-specific parameters */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						4
+#define Anum_pg_seqam_amname				1
+#define Anum_pg_seqam_amalloc				2
+#define Anum_pg_seqam_amsetval				3
+#define Anum_pg_seqam_amoptions				4
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 3971 (  local		sequence_local_alloc sequence_local_setval sequence_local_options));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 3971
+
+#define DEFAULT_SEQAM	""
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index ffafe6c..9b4ac6e 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -31,6 +31,7 @@ typedef struct FormData_pg_sequence
 	int64		log_cnt;
 	bool		is_cycled;
 	bool		is_called;
+	bytea		amdata;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -49,9 +50,10 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 #define SEQ_COL_LOG				8
 #define SEQ_COL_CYCLE			9
 #define SEQ_COL_CALLED			10
+#define SEQ_COL_AMDATA			11
 
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_AMDATA
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e5235cb..89a1727 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1886,16 +1886,20 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } CreateSeqStmt;
 
 typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 3e981b3..6e93258 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -376,6 +376,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_default_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 589c9a8..d930ac9 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -48,10 +49,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
+ * functions, defined by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+	/* pg_am only */
 	FmgrInfo	aminsert;
 	FmgrInfo	ambeginscan;
 	FmgrInfo	amgettuple;
@@ -61,6 +64,14 @@ typedef struct RelationAmInfo
 	FmgrInfo	ammarkpos;
 	FmgrInfo	amrestrpos;
 	FmgrInfo	amcanreturn;
+	FmgrInfo	amcostestimate;
+
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+
+	/* Common */
+	FmgrInfo	amoptions;
 } RelationAmInfo;
 
 
@@ -123,23 +134,25 @@ typedef struct RelationData
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+
 	/*
-	 * index access support info (used only for an index relation)
+	 * Access support info (used only for index or sequence relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
 	 * arrays are indexed by support function number, which is a sufficient
 	 * identifier given that restriction.
 	 *
-	 * Note: rd_amcache is available for index AMs to cache private data about
-	 * an index.  This must be just a cache since it may get reset at any time
+	 * Note: rd_amcache is available for AMs to cache private data about
+	 * an object.  This must be just a cache since it may get reset at any time
 	 * (in particular, it will get reset by a relcache inval message for the
-	 * index).	If used, it must point to a single memory chunk palloc'd in
+	 * object).	If used, it must point to a single memory chunk palloc'd in
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
-	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am */
+	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am or pg_seqam */
 	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
@@ -150,7 +163,7 @@ typedef struct RelationData
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
-	void	   *rd_amcache;		/* available for use by index AM */
+	void	   *rd_amcache;		/* available for use by AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
 
 	/*
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index e41b3d2..9b6f434 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 57d614f..b8e822e 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -51,9 +51,11 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR
        0::oid = ANY (p1.proargtypes) OR
        procost <= 0 OR
        CASE WHEN proretset THEN prorows <= 0 ELSE prorows != 0 END;
- oid | proname 
------+---------
-(0 rows)
+ oid  |        proname        
+------+-----------------------
+ 3939 | sequence_local_alloc
+ 3940 | sequence_local_setval
+(2 rows)
 
 -- prosrc should never be null or empty
 SELECT p1.oid, p1.proname
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index a62a3e3..fe6c3fe 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -122,6 +122,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8fcb700..39e3e9d 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -171,9 +171,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called | amdata 
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------+--------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f         | 
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -189,9 +189,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called | amdata 
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------+--------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t         | 
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c725bba..e2ee303 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -117,6 +117,7 @@ SELECT table_name, column_name, is_updatable
  ro_view20  | log_cnt       | NO
  ro_view20  | is_cycled     | NO
  ro_view20  | is_called     | NO
+ ro_view20  | amdata        | NO
  ro_view21  | a             | NO
  ro_view21  | b             | NO
  ro_view21  | g             | NO
@@ -140,7 +141,7 @@ SELECT table_name, column_name, is_updatable
  rw_view16  | a             | YES
  rw_view16  | b             | YES
  rw_view16  | aa            | YES
-(48 rows)
+(49 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/namespace.sql b/src/test/regress/sql/namespace.sql
index 879b6c3..2bb9134 100644
--- a/src/test/regress/sql/namespace.sql
+++ b/src/test/regress/sql/namespace.sql
@@ -1,7 +1,6 @@
 --
 -- Regression tests for schemas (namespaces)
 --
-
 CREATE SCHEMA test_schema_1
        CREATE UNIQUE INDEX abc_a_idx ON abc (a)
 
gapless.tarapplication/x-tar; name=gapless.tarDownload
seq_test.psqlapplication/octet-stream; name=seq_test.psqlDownload
#3Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Simon Riggs (#2)
Re: Sequence Access Method WIP

On 14.11.2013 22:10, Simon Riggs wrote:

On 16 January 2013 00:40, Simon Riggs <simon@2ndquadrant.com> wrote:

SeqAm allows you to specify a plugin that alters the behaviour for
sequence allocation and resetting, aimed specifically at clustering
systems.

New command options on end of statement allow syntax
CREATE SEQUENCE foo_seq
USING globalseq

Production version of this, ready for commit to PG 9.4

Includes test extension which allows sequences without gaps - "gapless".

Test using seq_test.psql after creating extension.

No dependencies on other patches.

It's pretty hard to review the this without seeing the "other"
implementation you're envisioning to use this API. But I'll try:

I wonder if the level of abstraction is right. The patch assumes that
the on-disk storage of all sequences is the same, so the access method
can't change that. But then it leaves the details of actually updating
the on-disk block, WAL-logging and all, as the responsibility of the
access method. Surely that's going to look identical in all the seqams,
if they all use the same on-disk format. That also means that the
sequence access methods can't be implemented as plugins, as plugins
can't do WAL-logging.

The comment in seqam.c says that there's a private column reserved for
access method-specific data, called am_data, but that seems to be the
only mention of am_data in the patch.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Andres Freund
andres@2ndquadrant.com
In reply to: Heikki Linnakangas (#3)
Re: Sequence Access Method WIP

On 2013-11-15 20:08:30 +0200, Heikki Linnakangas wrote:

It's pretty hard to review the this without seeing the "other"
implementation you're envisioning to use this API. But I'll try:

We've written a distributed sequence implementation against it, so it
works at least for that use case.

While certainly not release worthy, it still might be interesting to
look at.
http://git.postgresql.org/gitweb/?p=users/andresfreund/postgres.git;a=blob;f=contrib/bdr/bdr_seq.c;h=c9480c8021882f888e35080f0e7a7494af37ae27;hb=bdr

bdr_sequencer_fill_sequence pre-allocates ranges of values,
bdr_sequence_alloc returns them upon nextval().

I wonder if the level of abstraction is right. The patch assumes that the
on-disk storage of all sequences is the same, so the access method can't
change that. But then it leaves the details of actually updating the on-disk
block, WAL-logging and all, as the responsibility of the access method.
Surely that's going to look identical in all the seqams, if they all use the
same on-disk format. That also means that the sequence access methods can't
be implemented as plugins, as plugins can't do WAL-logging.

Well, it exposes log_sequence_tuple() - together with the added "am
private" column of pg_squence that allows to do quite a bit of different
things. I think unless we really implement pluggable RMGRs - which I
don't really see coming - we cannot be radically different.

The comment in seqam.c says that there's a private column reserved for
access method-specific data, called am_data, but that seems to be the only
mention of am_data in the patch.

It's amdata, not am_data :/. Directly at the end of pg_sequence.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Simon Riggs
simon@2ndQuadrant.com
In reply to: Heikki Linnakangas (#3)
Re: Sequence Access Method WIP

On 15 November 2013 15:08, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

I wonder if the level of abstraction is right.

That is the big question and not something to shy away from.

What is presented is not the first thought, by a long way. Andres'
contribution to the patch is mainly around this point, so the seq am
is designed with the needs of the main use case in mind.

I'm open to suggested changes but I would say that practical usage
beats changes suggested for purity.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Peter Eisentraut
peter_e@gmx.net
In reply to: Simon Riggs (#2)
Re: Sequence Access Method WIP

On 11/14/13, 3:10 PM, Simon Riggs wrote:

On 16 January 2013 00:40, Simon Riggs <simon@2ndquadrant.com> wrote:

SeqAm allows you to specify a plugin that alters the behaviour for
sequence allocation and resetting, aimed specifically at clustering
systems.

New command options on end of statement allow syntax
CREATE SEQUENCE foo_seq
USING globalseq

Production version of this, ready for commit to PG 9.4

This patch doesn't apply anymore.

Also, you set this to "returned with feedback" in the CF app. Please
verify whether that was intentional.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Simon Riggs
simon@2ndQuadrant.com
In reply to: Peter Eisentraut (#6)
Re: Sequence Access Method WIP

On 15 November 2013 15:48, Peter Eisentraut <peter_e@gmx.net> wrote:

On 11/14/13, 3:10 PM, Simon Riggs wrote:

On 16 January 2013 00:40, Simon Riggs <simon@2ndquadrant.com> wrote:

SeqAm allows you to specify a plugin that alters the behaviour for
sequence allocation and resetting, aimed specifically at clustering
systems.

New command options on end of statement allow syntax
CREATE SEQUENCE foo_seq
USING globalseq

Production version of this, ready for commit to PG 9.4

This patch doesn't apply anymore.

Yes, a patch conflict was just committed, will repost.

Also, you set this to "returned with feedback" in the CF app. Please
verify whether that was intentional.

Not sure that was me, if so, corrected.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Simon Riggs (#7)
Re: Sequence Access Method WIP

On 15.11.2013 21:00, Simon Riggs wrote:

On 15 November 2013 15:48, Peter Eisentraut <peter_e@gmx.net> wrote:

Also, you set this to "returned with feedback" in the CF app. Please
verify whether that was intentional.

Not sure that was me, if so, corrected.

It was me, sorry. I figured this needs such a large restructuring that
it's not going to be committable in this commitfest. But I'm happy to
keep the discussion going if you think otherwise.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Andres Freund (#4)
Re: Sequence Access Method WIP

On 15.11.2013 20:21, Andres Freund wrote:

On 2013-11-15 20:08:30 +0200, Heikki Linnakangas wrote:

It's pretty hard to review the this without seeing the "other"
implementation you're envisioning to use this API. But I'll try:

We've written a distributed sequence implementation against it, so it
works at least for that use case.

While certainly not release worthy, it still might be interesting to
look at.
https://urldefense.proofpoint.com/v1/url?u=http://git.postgresql.org/gitweb/?p%3Dusers/andresfreund/postgres.git%3Ba%3Dblob%3Bf%3Dcontrib/bdr/bdr_seq.c%3Bh%3Dc9480c8021882f888e35080f0e7a7494af37ae27%3Bhb%3Dbdr&amp;k=oIvRg1%2BdGAgOoM1BIlLLqw%3D%3D%0A&amp;r=xGch4oNJbpD%2BKPJECmgw4SLBhytSZLBX7UnkZhtNcpw%3D%0A&amp;m=Rbmo%2BDar4PZrQmGH2adz7cCgqRl2%2FXH845YIA1ifS7A%3D%0A&amp;s=8d287f35070fe7cb586f10b5bfe8664ad29e986b5f2d2286c4109e09f615668d

bdr_sequencer_fill_sequence pre-allocates ranges of values,
bdr_sequence_alloc returns them upon nextval().

Thanks. That pokes into the low-level details of sequence tuples, just
like I was afraid it would.

I wonder if the level of abstraction is right. The patch assumes that the
on-disk storage of all sequences is the same, so the access method can't
change that. But then it leaves the details of actually updating the on-disk
block, WAL-logging and all, as the responsibility of the access method.
Surely that's going to look identical in all the seqams, if they all use the
same on-disk format. That also means that the sequence access methods can't
be implemented as plugins, as plugins can't do WAL-logging.

Well, it exposes log_sequence_tuple() - together with the added "am
private" column of pg_squence that allows to do quite a bit of different
things. I think unless we really implement pluggable RMGRs - which I
don't really see coming - we cannot be radically different.

The proposed abstraction leaks like a sieve. The plugin should not need
to touch anything but the private amdata field. log_sequence_tuple() is
way too low-level.

Just as a thought-experiment, imagine that we decided to re-implement
sequences so that all the sequence values are stored in one big table,
or flat-file in the data directory, instead of the current
implementation where we have a one-block relation file for each
sequence. If the sequence access method API is any good, it should stay
unchanged. That's clearly not the case with the proposed API.

The comment in seqam.c says that there's a private column reserved for
access method-specific data, called am_data, but that seems to be the only
mention of am_data in the patch.

It's amdata, not am_data :/. Directly at the end of pg_sequence.

Ah, got it.

Stepping back a bit and looking at this problem from a higher level, why
do you need to hack this stuff into the sequences? Couldn't you just
define a new set of functions, say bdr_currval() and bdr_nextval(), to
operate on these new kinds of sequences? You're not making much use of
the existing sequence infrastructure, anyway, so it might be best to
just keep the implementation completely separate. If you need it for
compatibility with applications, you could create facade
currval/nextval() functions that call the built-in version or the bdr
version depending on the argument.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Andres Freund
andres@2ndquadrant.com
In reply to: Heikki Linnakangas (#9)
Re: Sequence Access Method WIP

On 2013-11-18 10:54:42 +0200, Heikki Linnakangas wrote:

On 15.11.2013 20:21, Andres Freund wrote:

Well, it exposes log_sequence_tuple() - together with the added "am
private" column of pg_squence that allows to do quite a bit of different
things. I think unless we really implement pluggable RMGRs - which I
don't really see coming - we cannot be radically different.

The proposed abstraction leaks like a sieve. The plugin should not need to
touch anything but the private amdata field. log_sequence_tuple() is way too
low-level.

Why? It's *less* low level than a good part of other crash recovery code
we have. I have quite some doubts about the layering, but it's imo
pretty sensible to centralize the wal logging code that plugins couldn't
write.

If you look at what e.g the _alloc callback currently gets passed.
a) Relation: Important for metadata like TupleDesc, name etc.
b) SeqTable entry: Important to setup state for cur/lastval
c) Buffer of the tuple: LSN etc.
d) HeapTuple itself: a place to store the tuples changed state

And if you then look at what gets modified in that callback:
Form_pg_sequence->amdata
->is_called
->last_value
->log_cnt
SeqTable ->last
SeqTable ->cached
SeqTable ->last_valid

You need is_called, last_valid because of our current representation of
sequences state (which we could change, to remove that need). The rest
contains values that need to be set depending on how you want your
sequence to behave:
* Amdata is obvious.
* You need log_cnt to influence/remember how big the chunks are you WAL log at
once are. Which e.g. you need to control if want a sequence that
doesn't leak values in crashes
* cached is needed to control the per-backend caching.

Just as a thought-experiment, imagine that we decided to re-implement
sequences so that all the sequence values are stored in one big table, or
flat-file in the data directory, instead of the current implementation where
we have a one-block relation file for each sequence. If the sequence access
method API is any good, it should stay unchanged. That's clearly not the
case with the proposed API.

I don't think we can entirely abstract away the reality that now they are
based on relations and might not be at some later point. Otherwise we'd
have to invent an API that copied all the data that's stored in struct
RelationData. Like name, owner, ...
Which non SQL accessible API we provide has a chance of providing that
level of consistency in the face of fundamental refactoring? I'd say
none.

Stepping back a bit and looking at this problem from a higher level, why do
you need to hack this stuff into the sequences? Couldn't you just define a
new set of functions, say bdr_currval() and bdr_nextval(), to operate on
these new kinds of sequences?

Basically two things:
a) User interface. For one everyone would need to reimplement the entire
sequence DDL from start. For another it means it's hard to write
(client) code that doesn't depend on a specific implementation.
b) It's not actually easy to get similar semantics in "userspace". How
would you emulate the crash-safe but non-transactional semantics of
sequences without copying most of sequence.c? Without writing
XLogInsert()s which we cannot do from a out-of-tree code?

You're not making much use of the existing sequence infrastructure, anyway, so it might be best to just keep the
implementation completely separate. If you need it for compatibility with
applications, you could create facade currval/nextval() functions that call
the built-in version or the bdr version depending on the argument.

That doesn't get you very far:
a) the default functions created by sequences are pg_catalog
prefixed. So you'd need to hack up the catalog to get your own functions
to be used if you want the application to work transparently. In which
you need to remember the former function because you now cannot call it
normally anymore. Yuck.
b) One would need nearly all of sequence.c again. You need the state
across calls, the locking, the WAL logging, DDL support. Pretty much
the only thing *not* used would be nextval_internal() and do_setval().

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Andres Freund (#10)
Re: Sequence Access Method WIP

On 18.11.2013 11:48, Andres Freund wrote:

On 2013-11-18 10:54:42 +0200, Heikki Linnakangas wrote:

On 15.11.2013 20:21, Andres Freund wrote:

Well, it exposes log_sequence_tuple() - together with the added "am
private" column of pg_squence that allows to do quite a bit of different
things. I think unless we really implement pluggable RMGRs - which I
don't really see coming - we cannot be radically different.

The proposed abstraction leaks like a sieve. The plugin should not need to
touch anything but the private amdata field. log_sequence_tuple() is way too
low-level.

Why? It's *less* low level than a good part of other crash recovery code
we have. I have quite some doubts about the layering, but it's imo
pretty sensible to centralize the wal logging code that plugins couldn't
write.

It doesn't go far enough, it's still too *low*-level. The sequence AM
implementation shouldn't need to have direct access to the buffer page
at all.

If you look at what e.g the _alloc callback currently gets passed.
a) Relation: Important for metadata like TupleDesc, name etc.
b) SeqTable entry: Important to setup state for cur/lastval
c) Buffer of the tuple: LSN etc.
d) HeapTuple itself: a place to store the tuples changed state

And if you then look at what gets modified in that callback:
Form_pg_sequence->amdata
->is_called
->last_value
->log_cnt
SeqTable ->last
SeqTable ->cached
SeqTable ->last_valid

You need is_called, last_valid because of our current representation of
sequences state (which we could change, to remove that need). The rest
contains values that need to be set depending on how you want your
sequence to behave:
* Amdata is obvious.
* You need log_cnt to influence/remember how big the chunks are you WAL log at
once are. Which e.g. you need to control if want a sequence that
doesn't leak values in crashes
* cached is needed to control the per-backend caching.

I don't think the sequence AM should be in control of 'cached'. The
caching is done outside the AM. And log_cnt probably should be passed to
the _alloc function directly as an argument, ie. the server code asks
the AM to allocate N new values in one call.

I'm thinking that the alloc function should look something like this:

seqam_alloc(Relation seqrel, int nrequested, Datum am_private)

And it should return:

int64 value - the first value allocated.
int nvalues - the number of values allocated.
am_private - updated private data.

The backend code handles the caching and logging of values. When it has
exhausted all the cached values (or doesn't have any yet), it calls the
AM's alloc function to get a new batch. The AM returns the new batch,
and updates its private state as necessary. Then the backend code
updates the relation file with the new values and the AM's private data.
WAL-logging and checkpointing is the backend's responsibility.

Just as a thought-experiment, imagine that we decided to re-implement
sequences so that all the sequence values are stored in one big table, or
flat-file in the data directory, instead of the current implementation where
we have a one-block relation file for each sequence. If the sequence access
method API is any good, it should stay unchanged. That's clearly not the
case with the proposed API.

I don't think we can entirely abstract away the reality that now they are
based on relations and might not be at some later point. Otherwise we'd
have to invent an API that copied all the data that's stored in struct
RelationData. Like name, owner, ...
Which non SQL accessible API we provide has a chance of providing that
level of consistency in the face of fundamental refactoring? I'd say
none.

I'm not thinking that we'd change sequences to not be relations. I'm
thinking that we might not want to store the state as a one-page file
anymore. In fact that was just discussed in the other thread titled
"init_sequence spill to hash table".

b) It's not actually easy to get similar semantics in "userspace". How
would you emulate the crash-safe but non-transactional semantics of
sequences without copying most of sequence.c? Without writing
XLogInsert()s which we cannot do from a out-of-tree code?

heap_inplace_update()

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Andres Freund
andres@2ndquadrant.com
In reply to: Heikki Linnakangas (#11)
Re: Sequence Access Method WIP

On 2013-11-18 12:50:21 +0200, Heikki Linnakangas wrote:

On 18.11.2013 11:48, Andres Freund wrote:
I don't think the sequence AM should be in control of 'cached'. The caching
is done outside the AM. And log_cnt probably should be passed to the _alloc
function directly as an argument, ie. the server code asks the AM to
allocate N new values in one call.

Sounds sane.

I'm thinking that the alloc function should look something like this:

seqam_alloc(Relation seqrel, int nrequested, Datum am_private)

I don't think we can avoid giving access to the other columns of
pg_sequence, stuff like increment, limits et all need to be available
for reading, so that'd also need to get passed in. And we need to signal
that am_private can be NULL, otherwise we'd end up with ugly ways to
signal that.
So I'd say to pass in the entire tuple, and return a copy? Alternatively
we can return am_private as a 'struct varlena *', so we can properly
signal empty values.

We also need a way to set am_private from outside
seqam_alloc/setval/... Many of the fancier sequences that people talked
about will need preallocation somewhere in the background. As proposed
that's easy enough to write using log_sequence_tuple(), this way we'd
need something that calls a callback with the appropriate buffer lock
held. So maybe a seqam_update(Relation seqrel, ...) callback that get's
called when somebody executes pg_sequence_update(oid)?

It'd probably a good idea to provide a generic function for checking
whether a new value falls in the boundaries of the sequence's min, max +
error handling.

I'm not thinking that we'd change sequences to not be relations. I'm
thinking that we might not want to store the state as a one-page file
anymore. In fact that was just discussed in the other thread titled
"init_sequence spill to hash table".

Yes, I read and even commented in that thread... But nothing in the
current proposed API would prevent you from going in that direction,
you'd just get passed in a different tuple/buffer.

b) It's not actually easy to get similar semantics in "userspace". How
would you emulate the crash-safe but non-transactional semantics of
sequences without copying most of sequence.c? Without writing
XLogInsert()s which we cannot do from a out-of-tree code?

heap_inplace_update()

That gets the crashsafe part partially (doesn't allow making the tuple
wider than before), but not the caching/stateful part et al. The point
is that you need most of sequence.c again.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Simon Riggs
simon@2ndQuadrant.com
In reply to: Heikki Linnakangas (#11)
Re: Sequence Access Method WIP

On 18 November 2013 07:50, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

It doesn't go far enough, it's still too *low*-level. The sequence AM
implementation shouldn't need to have direct access to the buffer page at
all.

I don't think the sequence AM should be in control of 'cached'. The caching
is done outside the AM. And log_cnt probably should be passed to the _alloc
function directly as an argument, ie. the server code asks the AM to
allocate N new values in one call.

I can't see what the rationale of your arguments is. All index Ams
write WAL and control buffer locking etc..

Do you have a new use case that shows why changes should happen? We
can't just redesign things based upon arbitrary decisions about what
things should or should not be possible via the API.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Simon Riggs (#13)
Re: Sequence Access Method WIP

On 18.11.2013 13:48, Simon Riggs wrote:

On 18 November 2013 07:50, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

It doesn't go far enough, it's still too *low*-level. The sequence AM
implementation shouldn't need to have direct access to the buffer page at
all.

I don't think the sequence AM should be in control of 'cached'. The caching
is done outside the AM. And log_cnt probably should be passed to the _alloc
function directly as an argument, ie. the server code asks the AM to
allocate N new values in one call.

I can't see what the rationale of your arguments is. All index Ams
write WAL and control buffer locking etc..

Index AM's are completely responsible for the on-disk structure, while
with the proposed API, both the AM and the backend are intimately aware
of the on-disk representation. Such a shared responsibility is not a
good thing in an API. I would also be fine with going 100% to the index
AM direction, and remove all knowledge of the on-disk layout from the
backend code and move it into the AMs. Then you could actually implement
the discussed "store all sequences in a single file" change by writing a
new sequence AM for it.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Simon Riggs (#2)
Re: Sequence Access Method WIP

On 14.11.2013 22:10, Simon Riggs wrote:

Includes test extension which allows sequences without gaps - "gapless".

I realize this is just for demonstration purposes, but it's worth noting
that it doesn't actually guarantee that when you use the sequence to
populate a column in the table, the column would not have gaps.
Sequences are not transactional, so rollbacks will still produce gaps.
The documentation is misleading on that point. Without a strong
guarantee, it's a pretty useless extension.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Simon Riggs
simon@2ndQuadrant.com
In reply to: Heikki Linnakangas (#15)
Re: Sequence Access Method WIP

On 18 November 2013 07:36, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

On 14.11.2013 22:10, Simon Riggs wrote:

Includes test extension which allows sequences without gaps - "gapless".

I realize this is just for demonstration purposes, but it's worth noting
that it doesn't actually guarantee that when you use the sequence to
populate a column in the table, the column would not have gaps. Sequences
are not transactional, so rollbacks will still produce gaps. The
documentation is misleading on that point. Without a strong guarantee, it's
a pretty useless extension.

True.

If I fix that problem, I should change the name to "lockup" sequences,
since only one transaction at a time could use the nextval.

Should I change the documentation, or just bin the idea?

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Simon Riggs
simon@2ndQuadrant.com
In reply to: Heikki Linnakangas (#14)
Re: Sequence Access Method WIP

On 18 November 2013 07:06, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

On 18.11.2013 13:48, Simon Riggs wrote:

On 18 November 2013 07:50, Heikki Linnakangas <hlinnakangas@vmware.com>
wrote:

It doesn't go far enough, it's still too *low*-level. The sequence AM
implementation shouldn't need to have direct access to the buffer page at
all.

I don't think the sequence AM should be in control of 'cached'. The
caching
is done outside the AM. And log_cnt probably should be passed to the
_alloc
function directly as an argument, ie. the server code asks the AM to
allocate N new values in one call.

I can't see what the rationale of your arguments is. All index Ams
write WAL and control buffer locking etc..

Index AM's are completely responsible for the on-disk structure, while with
the proposed API, both the AM and the backend are intimately aware of the
on-disk representation. Such a shared responsibility is not a good thing in
an API. I would also be fine with going 100% to the index AM direction, and
remove all knowledge of the on-disk layout from the backend code and move it
into the AMs. Then you could actually implement the discussed "store all
sequences in a single file" change by writing a new sequence AM for it.

I think the way to resolve this is to do both of these things, i.e. a
two level API

1. Implement SeqAM API at the most generic level. Add a nextval() call
as well as alloc()

2. Also implement the proposed changes to alloc()

So the SeqAM would implement either nextval() or alloc() but not both

global sequences as envisaged for BDR would use a special alloc() call.

I don't think that is too much work, but I want to do this just once...

Thoughts on exact next steps for implementation please?

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Simon Riggs (#17)
Re: Sequence Access Method WIP

On 24.11.2013 19:23, Simon Riggs wrote:

On 18 November 2013 07:06, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

On 18.11.2013 13:48, Simon Riggs wrote:

On 18 November 2013 07:50, Heikki Linnakangas <hlinnakangas@vmware.com>
wrote:

It doesn't go far enough, it's still too *low*-level. The sequence AM
implementation shouldn't need to have direct access to the buffer page at
all.

I don't think the sequence AM should be in control of 'cached'. The
caching
is done outside the AM. And log_cnt probably should be passed to the
_alloc
function directly as an argument, ie. the server code asks the AM to
allocate N new values in one call.

I can't see what the rationale of your arguments is. All index Ams
write WAL and control buffer locking etc..

Index AM's are completely responsible for the on-disk structure, while with
the proposed API, both the AM and the backend are intimately aware of the
on-disk representation. Such a shared responsibility is not a good thing in
an API. I would also be fine with going 100% to the index AM direction, and
remove all knowledge of the on-disk layout from the backend code and move it
into the AMs. Then you could actually implement the discussed "store all
sequences in a single file" change by writing a new sequence AM for it.

I think the way to resolve this is to do both of these things, i.e. a
two level API

1. Implement SeqAM API at the most generic level. Add a nextval() call
as well as alloc()

2. Also implement the proposed changes to alloc()

The proposed changes to alloc() would still suffer from all the problems
that I complained about. Adding a new API alongside doesn't help with that.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Simon Riggs
simon@2ndQuadrant.com
In reply to: Heikki Linnakangas (#18)
Re: Sequence Access Method WIP

On 25 November 2013 04:01, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

The proposed changes to alloc() would still suffer from all the problems
that I complained about. Adding a new API alongside doesn't help with that.

You made two proposals. I suggested implementing both.

What would you have me do?

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Simon Riggs (#16)
Re: Sequence Access Method WIP

On 11/24/13 19:15, Simon Riggs wrote:

On 18 November 2013 07:36, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

On 14.11.2013 22:10, Simon Riggs wrote:

Includes test extension which allows sequences without gaps - "gapless".

I realize this is just for demonstration purposes, but it's worth noting
that it doesn't actually guarantee that when you use the sequence to
populate a column in the table, the column would not have gaps. Sequences
are not transactional, so rollbacks will still produce gaps. The
documentation is misleading on that point. Without a strong guarantee, it's
a pretty useless extension.

True.

If I fix that problem, I should change the name to "lockup" sequences,
since only one transaction at a time could use the nextval.

Should I change the documentation, or just bin the idea?

Just bin it. It would be useful if it could guarantee gaplessness, but I
don't see how to do that.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Simon Riggs (#19)
Re: Sequence Access Method WIP

On 11/25/13 12:00, Simon Riggs wrote:

On 25 November 2013 04:01, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

The proposed changes to alloc() would still suffer from all the problems
that I complained about. Adding a new API alongside doesn't help with that.

You made two proposals. I suggested implementing both.

What would you have me do?

Dunno. I do know that the proposed changes to alloc() are not a good
API. You could implement the other API, I think that has a chance of
being a cleaner API, but looking at the BDR extension that would use
that facility, I'm not sure how useful that would be for you. (and I'd
really like to see an actual implementation of whatever API we come up
with, before we commit to maintaining it).

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#11)
Re: Sequence Access Method WIP

On 18/11/13 11:50, Heikki Linnakangas wrote:

I don't think the sequence AM should be in control of 'cached'. The
caching is done outside the AM. And log_cnt probably should be passed to
the _alloc function directly as an argument, ie. the server code asks
the AM to allocate N new values in one call.

I'm thinking that the alloc function should look something like this:

seqam_alloc(Relation seqrel, int nrequested, Datum am_private)

I was looking at this a bit today and what I see is that it's not that
simple.

Minimum input the seqam_alloc needs is:
- Relation seqrel
- int64 minv, maxv, incby, bool is_cycled - these are basically options
giving info about how the new numbers are allocated (I guess some
implementations are not going to support all of those)
- bool is_called - the current built-in sequence generator behaves
differently based on it and I am not sure we can get over it (it could
perhaps be done in back-end independently of AM?)
- int64 nrequested - number of requested values
- Datum am_private - current private data

In this light I agree with what Andres wrote - let's just send the whole
Form_pg_sequence object.

Also makes me think that the seqam options interface should also be
passed the minv/maxv/incby/is_cycled etc options for validation, not
just the amoptions.

And it should return:

int64 value - the first value allocated.
int nvalues - the number of values allocated.
am_private - updated private data.

There is also more needed than this, you need:
- int64 value - first value allocated (value to be returned)
- int64 nvalues - number of values allocated
- int64 last - last cached value (used for cached/last_value)
- int64 next - last logged value (used for wal logging)
- am_private - updated private data, must be possible to return as null

I personally don't like that we need all the "nvalues", "next" and
"last" as it makes the seqam a little bit too aware of the sequence
logging internals in my opinion but I haven't found a way around it -
it's impossible for backend to know how the AM will act around
incby/maxv/minv/cycling so it can't really calculate these values by
itself, unless ofcourse we fix the behavior and require seqams to behave
predictably, but that somewhat breaks the whole idea of leaving the
allocation to the seqam. Obviously it would also work to return list of
allocated values and then backend could calculate the "value",
"nvalues", "last", "next" from that list by itself, but I am worried
about performance of that approach.

The backend code handles the caching and logging of values. When it has
exhausted all the cached values (or doesn't have any yet), it calls the
AM's alloc function to get a new batch. The AM returns the new batch,
and updates its private state as necessary. Then the backend code
updates the relation file with the new values and the AM's private data.
WAL-logging and checkpointing is the backend's responsibility.

Agreed here.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Andres Freund
andres@2ndquadrant.com
In reply to: Petr Jelinek (#22)
Re: Sequence Access Method WIP

On 2014-09-15 01:38:52 +0200, Petr Jelinek wrote:

- int64 minv, maxv, incby, bool is_cycled - these are basically options
giving info about how the new numbers are allocated (I guess some
implementations are not going to support all of those)
- bool is_called - the current built-in sequence generator behaves
differently based on it and I am not sure we can get over it (it could
perhaps be done in back-end independently of AM?)

I think we might be able to get rid of is_called entirely. Or at least
get rid of it from the view of the AMs.

Also makes me think that the seqam options interface should also be passed
the minv/maxv/incby/is_cycled etc options for validation, not just the
amoptions.

Sup.

BTW: Is 'is_cycled' a horrible name, or is that just me? Horribly easy
to confuse with the fact that a sequence has already wrapped around...

And it should return:

int64 value - the first value allocated.
int nvalues - the number of values allocated.
am_private - updated private data.

There is also more needed than this, you need:
- int64 value - first value allocated (value to be returned)
- int64 nvalues - number of values allocated
- int64 last - last cached value (used for cached/last_value)
- int64 next - last logged value (used for wal logging)
- am_private - updated private data, must be possible to return as null

I personally don't like that we need all the "nvalues", "next" and "last" as
it makes the seqam a little bit too aware of the sequence logging internals
in my opinion but I haven't found a way around it - it's impossible for
backend to know how the AM will act around incby/maxv/minv/cycling so it
can't really calculate these values by itself, unless ofcourse we fix the
behavior and require seqams to behave predictably, but that somewhat breaks
the whole idea of leaving the allocation to the seqam. Obviously it would
also work to return list of allocated values and then backend could
calculate the "value", "nvalues", "last", "next" from that list by itself,
but I am worried about performance of that approach.

Yea, it's far from pretty.

I'm not convinced that the AM ever needs to/should care about
caching. To me that's more like a generic behaviour. So it probably
should be abstracted away from the individual AMs.

I think the allocation routine might also need to be able to indicate
whether WAL logging is needed or not.

One thing I want attention to be paid to is that the interface should be
able to support 'gapless' sequences. I.e. where nextval() (and thus
alloc) needs to wait until the last caller to it finished. That very
well can be relevant from the locking *and* WAL logging perspective.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Petr Jelinek
petr@2ndquadrant.com
In reply to: Andres Freund (#23)
Re: Sequence Access Method WIP

On 16/09/14 14:17, Andres Freund wrote:

On 2014-09-15 01:38:52 +0200, Petr Jelinek wrote:

There is also more needed than this, you need:
- int64 value - first value allocated (value to be returned)
- int64 nvalues - number of values allocated
- int64 last - last cached value (used for cached/last_value)
- int64 next - last logged value (used for wal logging)
- am_private - updated private data, must be possible to return as null

I personally don't like that we need all the "nvalues", "next" and "last" as
it makes the seqam a little bit too aware of the sequence logging internals
in my opinion but I haven't found a way around it - it's impossible for
backend to know how the AM will act around incby/maxv/minv/cycling so it
can't really calculate these values by itself, unless ofcourse we fix the
behavior and require seqams to behave predictably, but that somewhat breaks
the whole idea of leaving the allocation to the seqam. Obviously it would
also work to return list of allocated values and then backend could
calculate the "value", "nvalues", "last", "next" from that list by itself,
but I am worried about performance of that approach.

Yea, it's far from pretty.

I'm not convinced that the AM ever needs to/should care about
caching. To me that's more like a generic behaviour. So it probably
should be abstracted away from the individual AMs.

I think the allocation routine might also need to be able to indicate
whether WAL logging is needed or not.

Well that means we probably want to return first allocated value, last
allocated value and then some boolean that tells backend if to wal log
the sequence or not (number of values allocated does not really seem to
be important unless I am missing something).

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Petr Jelinek
petr@2ndquadrant.com
In reply to: Andres Freund (#23)
1 attachment(s)
Re: Sequence Access Method WIP

Hi,

I rewrote the patch with different API along the lines of what was
discussed.

The API now consists of following functions:
sequence_alloc - allocating range of new values
The function receives the sequence relation, current value, number of
requested values amdata and relevant sequence options like min/max and
returns new amdata, new current value, number of values allocated and
also if it needs wal write (that should be returned if amdata has
changed plus other reasons the AM might have to force the wal update).

sequence_setval - notification that setval is happening
This function gets sequence relation, previous value and new value plus
the amdata and returns amdata (I can imagine some complex sequence AMs
will want to throw error that setval can't be done on them).

sequence_request_update/sequence_update - used for background processing
Basically AM can call the sequence_request_update and backend will then
call the sequence_update method of an AM with current amdata and will
write the updated amdata to disk

sequence_seqparams - function to process/validate the standard sequence
options like start position, min/max, increment by etc by the AM, it's
called in addition to the standard processing

sequence_reloptions - this is the only thing that remained unchanged
from previous patch, it's meant to pass custom options to the AM

Only the alloc and reloptions methods are required (and implemented by
the local AM).

The caching, xlog writing, updating the page, etc is handled by backend,
the AM does not see the tuple at all. I decided to not pass even the
struct around and just pass the relevant options because I think if we
want to abstract the storage properly then the AM should not care about
how the pg_sequence looks like at all, even if it means that the
sequence_alloc parameter list is bit long.

For the amdata handling (which is the AM's private data variable) the
API assumes that (Datum) 0 is NULL, this seems to work well for
reloptions so should work here also and it simplifies things a little
compared to passing pointers to pointers around and making sure
everything is allocated, etc.

Sadly the fact that amdata is not fixed size and can be NULL made the
page updates of the sequence relation quite more complex that it used to
be. There are probably some optimizations possible there but I think the
patch is good enough for the review now, so I am adding it to October
commitfest.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

seqamv3.patchtext/x-diff; name=seqamv3.patchDownload
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index c32088f..aea4a14 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,6 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = common gin gist hash heap index nbtree rmgrdesc spgist transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index e0b81b9..c5b7e0a 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -306,6 +306,7 @@ static bool need_initialization = true;
 static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 					int text_len, bool validate);
+static bytea *common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate);
 
 /*
  * initialize_reloptions
@@ -806,7 +807,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -839,6 +841,9 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 		case RELKIND_INDEX:
 			options = index_reloptions(amoptions, datum, false);
 			break;
+		case RELKIND_SEQUENCE:
+			options = sequence_reloptions(amoptions, datum, false);
+			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
 			break;
@@ -1284,13 +1289,31 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 /*
  * Parse options for indexes.
+ */
+bytea *
+index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for sequences.
+ */
+bytea *
+sequence_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
-bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+static bytea *
+common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..f782f7e
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..552225a
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,351 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAm. That is also the basic default.  Local
+ * SeqAm assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAm mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAm would also wish to control node-level caches for sequences to
+ * ensure good performance.  The CACHE option and other options may be
+ * overridden by the _alloc API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAm allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module.  The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, am_data.
+ *
+ * We currently assume that there is no persistent state held within the SeqAm,
+ * so it is safe to ALTER the access method of an object without taking any
+ * special actions, as long as we hold an AccessExclusiveLock while we do that.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAm data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*default_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqRelation->rd_aminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqRelation->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqRelation->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Manager API
+ *
+ *  INTERFACE ROUTINES
+ *		sequence_alloc		- allocate a new range of values for the sequence
+ *		sequence_setval		- coordinate the reset of a sequence to new value
+ *		sequence_update		- callback for background processing (called
+ *		                      by sequence_request_update)
+ *		sequence_seqparams	- process the standard sequence parameters
+ *
+ *		sequence_reloptions	- process reloptions - located in reloptions.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * sequence_alloc - allocate sequence values in a sequence
+ */
+Datum
+sequence_alloc(Relation seqRelation, int64 *current_value, int64 nrequested,
+			   int64 min_value, int64 max_value, int64 increment_by,
+			   bool is_cycled, Datum amdata, int64 *nallocated,
+			   bool *xlog_needed)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqRelation));
+	Assert(PointerIsValid(seqRelation->rd_seqam));
+	Assert(OidIsValid(seqRelation->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 10, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqRelation);
+	fcinfo.arg[1] = PointerGetDatum(current_value);
+	fcinfo.arg[2] = Int64GetDatum(nrequested);
+	fcinfo.arg[3] = Int64GetDatum(min_value);
+	fcinfo.arg[4] = Int64GetDatum(max_value);
+	fcinfo.arg[5] = Int64GetDatum(increment_by);
+	fcinfo.arg[6] = BoolGetDatum(is_cycled);
+	fcinfo.arg[7] = amdata;
+	fcinfo.arg[8] = PointerGetDatum(nallocated);
+	fcinfo.arg[9] = PointerGetDatum(xlog_needed);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[4] = false;
+	fcinfo.argnull[5] = false;
+	fcinfo.argnull[6] = false;
+	fcinfo.argnull[7] = false;
+	fcinfo.argnull[8] = false;
+	fcinfo.argnull[9] = false;
+
+	return FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * sequence_setval - set sequence values in a sequence
+ */
+Datum
+sequence_setval(Relation seqRelation, int64 current_value, int64 new_value,
+				Datum amdata)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqRelation));
+	Assert(PointerIsValid(seqRelation->rd_seqam));
+	Assert(OidIsValid(seqRelation->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return amdata;
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqRelation);
+	fcinfo.arg[1] = Int64GetDatum(current_value);
+	fcinfo.arg[2] = Int64GetDatum(new_value);
+	fcinfo.arg[3] = amdata;
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+
+	return FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * sequence_update - callback for sequence_request_update
+ */
+Datum
+sequence_update(Relation seqRelation, Datum amdata)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqRelation));
+	Assert(PointerIsValid(seqRelation->rd_seqam));
+	Assert(OidIsValid(seqRelation->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamupdate, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return amdata;
+
+	/*
+	 * have the seqam's update proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 2, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqRelation);
+	fcinfo.arg[1] = amdata;
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+
+	return FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * sequence_seqparams - process standard sequence params
+ */
+Datum
+sequence_seqparams(RegProcedure amoptions, List *params, Datum amdata,
+				   bool isInit)
+{
+	FmgrInfo   procedure;
+	FunctionCallInfoData fcinfo;
+
+	if (!RegProcedureIsValid(amoptions))
+		return amdata;
+
+	fmgr_info(amoptions, &procedure);
+
+	/*
+	 * have the seqam's seqparams proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 3, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(params);
+	fcinfo.arg[1] = amdata;
+	fcinfo.arg[2] = BoolGetDatum(isInit);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	return FunctionCallInvoke(&fcinfo);
+}
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new default_sequenceam */
+bool
+check_default_seqam(char **newval, void **extra, GucSource source)
+{
+	if (**newval == '\0')
+		return true;
+
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.	Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence manager \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence manager \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+/*
+ * GetDefaultSeqAM -- get the OID of the current default sequence AM
+ *
+ * This exists to hide (and possibly optimize the use of) the
+ * default_seqam GUC variable.
+ */
+Oid
+GetDefaultSeqAM(void)
+{
+	/* Fast path for default_tablespace == "" */
+	if (default_seqam == NULL || default_seqam[0] == '\0')
+		return LOCAL_SEQAM_OID;
+
+	return get_seqam_oid(default_seqam, false);
+}
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+	if (!HeapTupleIsValid(tuple))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("squence am \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index b257b02..3a6a597 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 3b89dd0..7078f9c 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,13 +14,19 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -72,6 +78,7 @@ typedef struct SeqTableData
 	/* if last != cached, we have not used up all the cached values */
 	int64		increment;		/* copy of sequence's increment field */
 	/* note that increment is zero until we first do read_seq_tuple() */
+	Datum		amdata;			/* private data of the SeqAm */
 } SeqTableData;
 
 typedef SeqTableData *SeqTable;
@@ -95,7 +102,14 @@ static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
 static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
-
+static void log_sequence_tuple(Relation seqrel, HeapTuple tup, Page page);
+static void  replace_sequence_tuple(Relation seqrel, Buffer buf,
+									HeapTuple tuple, HeapTuple newtup);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static HeapTuple seqtup_update_amdata(HeapTuple tuple, TupleDesc tupdesc, Datum amdata);
+static Datum init_seqam(Oid oldAM, Oid *newAM, char *accessMethod,
+						List *seqoptions, List *reloptions, Datum amdata,
+						bool isInit);
 
 /*
  * DefineSequence
@@ -108,6 +122,7 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	Relation	rel;
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
@@ -115,6 +130,7 @@ DefineSequence(CreateSeqStmt *seq)
 	bool		null[SEQ_COL_LASTCOL];
 	int			i;
 	NameData	name;
+	Datum		amdata;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -140,8 +156,10 @@ DefineSequence(CreateSeqStmt *seq)
 		}
 	}
 
-	/* Check and set all option values */
+	/* Check and set all param values */
 	init_params(seq->options, true, &new, &owned_by);
+	amdata = init_seqam(InvalidOid, &seqamid, seq->accessMethod,
+						seq->options, seq->amoptions, (Datum) 0, true);
 
 	/*
 	 * Create relation (and fill value[] and null[] for the tuple)
@@ -218,6 +236,14 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "is_called";
 				value[i - 1] = BoolGetDatum(false);
 				break;
+			case SEQ_COL_AMDATA:
+				coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+				coldef->colname = "amdata";
+				if (PointerIsValid(DatumGetPointer(amdata)))
+					value[i - 1] = amdata;
+				else
+					null[i - 1] = true;
+				break;
 		}
 		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
@@ -233,6 +259,14 @@ DefineSequence(CreateSeqStmt *seq)
 	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
 	Assert(seqoid != InvalidOid);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	rel = heap_open(seqoid, AccessExclusiveLock);
 	tupDesc = RelationGetDescr(rel);
 
@@ -270,6 +304,8 @@ ResetSequence(Oid seq_relid)
 	Buffer		buf;
 	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Datum		amdata;
+	bool		amdata_isnull;
 
 	/*
 	 * Read the old sequence.  This does a bit more work than really
@@ -279,10 +315,21 @@ ResetSequence(Oid seq_relid)
 	init_sequence(seq_relid, &elm, &seq_rel);
 	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
 
-	/*
-	 * Copy the existing sequence tuple.
-	 */
-	tuple = heap_copytuple(&seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(&seqtuple);
+
+	/* Get original amdata. */
+	amdata = fastgetattr(&seqtuple, SEQ_COL_AMDATA,
+						 RelationGetDescr(seq_rel), &amdata_isnull);
+	if (amdata_isnull)
+		amdata = (Datum) 0;
+
+	/* Call into the sequence AM. */
+	amdata = sequence_setval(seq_rel, seq->last_value,
+							 seq->start_value, amdata);
+
+	/* Build new tuple with updated amdata and point seq to it. */
+	tuple = seqtup_update_amdata(&seqtuple, RelationGetDescr(seq_rel), amdata);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Now we're done with the old page */
 	UnlockReleaseBuffer(buf);
@@ -291,7 +338,6 @@ ResetSequence(Oid seq_relid)
 	 * Modify the copied tuple to execute the restart (compare the RESTART
 	 * action in AlterSequence)
 	 */
-	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 	seq->last_value = seq->start_value;
 	seq->is_called = false;
 	seq->log_cnt = 0;
@@ -305,7 +351,9 @@ ResetSequence(Oid seq_relid)
 							  InvalidMultiXactId);
 
 	/*
-	 * Insert the modified tuple into the new storage file.
+	 * Insert the modified tuple into the new storage file. This will log
+	 * superflously log the old values, but this isn't a performance critical
+	 * path...
 	 */
 	fill_seq_with_data(seq_rel, tuple);
 
@@ -366,27 +414,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		XLogRecData rdata[2];
-
-		xlrec.node = rel->rd_node;
-		rdata[0].data = (char *) &xlrec;
-		rdata[0].len = sizeof(xl_seq_rec);
-		rdata[0].buffer = InvalidBuffer;
-		rdata[0].next = &(rdata[1]);
-
-		rdata[1].data = (char *) tuple->t_data;
-		rdata[1].len = tuple->t_len;
-		rdata[1].buffer = InvalidBuffer;
-		rdata[1].next = NULL;
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, page);
 
 	END_CRIT_SECTION();
 
@@ -402,13 +430,17 @@ Oid
 AlterSequence(AlterSeqStmt *stmt)
 {
 	Oid			relid;
+	Oid			seqamid;
 	SeqTable	elm;
 	Relation	seqrel;
 	Buffer		buf;
 	HeapTupleData seqtuple;
+	HeapTuple	tuple;
 	Form_pg_sequence seq;
 	FormData_pg_sequence new;
 	List	   *owned_by;
+	Datum		amdata;
+	bool		amdata_isnull;
 
 	/* Open and lock sequence. */
 	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
@@ -433,45 +465,46 @@ AlterSequence(AlterSeqStmt *stmt)
 	/* Copy old values of options into workspace */
 	memcpy(&new, seq, sizeof(FormData_pg_sequence));
 
+	/* Get original amdata. */
+	amdata = fastgetattr(&seqtuple, SEQ_COL_AMDATA,
+						 RelationGetDescr(seqrel), &amdata_isnull);
+	if (amdata_isnull)
+		amdata = (Datum) 0;
+
 	/* Check and set new values */
 	init_params(stmt->options, false, &new, &owned_by);
+	amdata = init_seqam(seqrel->rd_rel->relam, &seqamid, stmt->accessMethod,
+						stmt->options, stmt->amoptions, amdata, false);
+
+	/*
+	 * Change the SeqAm, if requested, using a transactional update.
+	 */
+	seqrel_update_relam(relid, seqamid);
+
+	/* Build new tuple with updated amdata and point seq to it. */
+	tuple = seqtup_update_amdata(&seqtuple, RelationGetDescr(seqrel), amdata);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
+
+	/*
+	 * Copy values.
+	 * XXX: there has to be a better way...
+	 */
+	seq->last_value = new.last_value;
+	seq->start_value = new.start_value;
+	seq->increment_by = new.increment_by;
+	seq->max_value = new.max_value;
+	seq->min_value = new.min_value;
+	seq->cache_value = new.cache_value;
+	seq->log_cnt = new.log_cnt;
+	seq->is_cycled = new.is_cycled;
+	seq->is_called = new.is_called;
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
 	elm->cached = elm->last;
 
 	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
-
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		XLogRecData rdata[2];
-		Page		page = BufferGetPage(buf);
-
-		xlrec.node = seqrel->rd_node;
-		rdata[0].data = (char *) &xlrec;
-		rdata[0].len = sizeof(xl_seq_rec);
-		rdata[0].buffer = InvalidBuffer;
-		rdata[0].next = &(rdata[1]);
-
-		rdata[1].data = (char *) seqtuple.t_data;
-		rdata[1].len = seqtuple.t_len;
-		rdata[1].buffer = InvalidBuffer;
-		rdata[1].next = NULL;
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	replace_sequence_tuple(seqrel, buf, &seqtuple, tuple);
 
 	UnlockReleaseBuffer(buf);
 
@@ -486,6 +519,49 @@ AlterSequence(AlterSeqStmt *stmt)
 	return relid;
 }
 
+/*
+ * sequence_request_update
+ *
+ * This function should be used by SeqAMs to request update callback
+ * when they need to do background processing on the sequence.
+ */
+void
+sequence_request_update(Oid seq_relid)
+{
+	Relation	seq_rel;
+	SeqTable	elm;
+	Buffer		buf;
+	HeapTupleData seqtuple;
+	HeapTuple	tuple;
+	Datum		amdata;
+	bool		amdata_isnull;
+
+	/*
+	 * Read the old sequence.  This does a bit more work than really
+	 * necessary, but it's simple, and we do want to double-check that it's
+	 * indeed a sequence.
+	 */
+	init_sequence(seq_relid, &elm, &seq_rel);
+	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+
+	/* Get original amdata. */
+	amdata = fastgetattr(&seqtuple, SEQ_COL_AMDATA,
+						 RelationGetDescr(seq_rel), &amdata_isnull);
+	if (amdata_isnull)
+		amdata = (Datum) 0;
+
+	/* Call into the sequence AM. */
+	amdata = sequence_update(seq_rel, amdata);
+
+	/* Build new tuple with updated amdata. */
+	tuple = seqtup_update_amdata(&seqtuple, RelationGetDescr(seq_rel), amdata);
+
+	replace_sequence_tuple(seq_rel, buf, &seqtuple, tuple);
+
+	/* Now we're done with the old page */
+	UnlockReleaseBuffer(buf);
+	relation_close(seq_rel, NoLock);
+}
 
 /*
  * Note: nextval with a text argument is no longer exported as a pg_proc
@@ -522,6 +598,10 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
@@ -535,13 +615,15 @@ nextval_internal(Oid relid)
 				maxv,
 				minv,
 				cache,
-				log,
 				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+				log,
+				value,
+				nallocated = 0,
+				last,
+				result;
+	bool		logit = false,
+				is_cycled;
+	Datum		amdata = (Datum) 0;
 
 	/* open and AccessShareLock sequence */
 	init_sequence(relid, &elm, &seqrel);
@@ -571,18 +653,16 @@ nextval_internal(Oid relid)
 	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
 	page = BufferGetPage(buf);
 
-	last = next = result = seq->last_value;
+	value = seq->last_value;
 	incby = seq->increment_by;
 	maxv = seq->max_value;
 	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
+	is_cycled = seq->is_cycled;
+	fetch = seq->cache_value;
 	log = seq->log_cnt;
 
 	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
 		fetch--;
-	}
 
 	/*
 	 * Decide whether we should emit a WAL log record.  If so, force up the
@@ -612,72 +692,39 @@ nextval_internal(Oid relid)
 		}
 	}
 
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
+	/* Call into sequnce AM code. */
+	if (fetch)
 	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+		bool	amdata_isnull;
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+		/* Get original amdata. */
+		amdata = fastgetattr(&seqtuple, SEQ_COL_AMDATA,
+							 RelationGetDescr(seqrel), &amdata_isnull);
+		if (amdata_isnull)
+			amdata = (Datum) 0;
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
+		amdata = sequence_alloc(seqrel, &value, fetch, minv, maxv, incby,
+								is_cycled, amdata, &nallocated, &logit);
+	}
+
+	cache = Min(seq->cache_value, nallocated);
+
+	if (!seq->is_called)
+		result = seq->last_value;		/* return last_value if not is_called */
+	else
+	{
+		Assert(nallocated);
+		result = value;
+		cache--;
 	}
 
-	log -= fetch;				/* adjust for any unfetched numbers */
+	log -= cache + (fetch-nallocated);
 	Assert(log >= 0);
 
+	last = result;
+	sequence_increment(seqrel, &last, cache, minv, maxv,
+					   incby, is_cycled, false);
+
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
 	elm->cached = last;			/* last fetched number */
@@ -685,53 +732,67 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
 	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
+	if (logit)
 	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		XLogRecData rdata[2];
-
 		/*
 		 * We don't log the current state of the tuple, but rather the state
 		 * as it would appear after "log" more fetches.  This lets us skip
 		 * that many future WAL records, at the cost that we lose those
 		 * sequence values if we crash.
 		 */
+		Form_pg_sequence logseq;
+		Page		temppage,
+					page;
+		HeapTuple	tuple;
+		int64		next = value;
+
+		sequence_increment(seqrel, &next, nallocated, minv, maxv,
+						   incby, is_cycled, false);
+
+		/* Build new tuple with updated amdata. */
+		tuple = seqtup_update_amdata(&seqtuple, RelationGetDescr(seqrel), amdata);
+		logseq = (Form_pg_sequence) GETSTRUCT(tuple);
+
+		logseq->last_value = next;
+		logseq->is_called = true;
+		logseq->log_cnt = 0;
 
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
+		/* Replace the original tuple with the updated one in-place */
+		page = BufferGetPage(buf);
 
-		xlrec.node = seqrel->rd_node;
-		rdata[0].data = (char *) &xlrec;
-		rdata[0].len = sizeof(xl_seq_rec);
-		rdata[0].buffer = InvalidBuffer;
-		rdata[0].next = &(rdata[1]);
+		temppage = PageGetTempPageCopySpecial(page);
 
-		rdata[1].data = (char *) seqtuple.t_data;
-		rdata[1].len = seqtuple.t_len;
-		rdata[1].buffer = InvalidBuffer;
-		rdata[1].next = NULL;
+		if (PageAddItem(temppage, (Item) tuple->t_data, tuple->t_len,
+						FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+			elog(PANIC, "replace_sequence_tuple: failed to add item to page");
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
+		PageSetLSN(temppage, PageGetLSN(page));
 
-		PageSetLSN(page, recptr);
+		START_CRIT_SECTION();
+
+		PageRestoreTempPage(temppage, page);
+
+		/*
+		 * We must mark the buffer dirty before doing XLogInsert(); see notes in
+		 * SyncOneBuffer(). However, we changed the buffer to the contents of
+		 * a "future" state. This looks like a violation of the buffer update
+		 * protocol, but it is in fact safe because we hold exclusive lock on
+		 * the buffer.  Any other process, including a checkpoint, that tries
+		 * to examine the buffer contents will block until we release the lock,
+		 * and then will see the final state that we install below.
+		 */
+		MarkBufferDirty(buf);
+
+		seqtuple.t_len = tuple->t_len;
+
+		log_sequence_tuple(seqrel, &seqtuple, page);
+	}
+	else
+	{
+		/* the seq changes should mark the buffer dirty in any case */
+		START_CRIT_SECTION();
+		MarkBufferDirty(buf);
 	}
 
 	/* Now update sequence tuple to the intended final state */
@@ -748,6 +809,7 @@ nextval_internal(Oid relid)
 	return result;
 }
 
+
 Datum
 currval_oid(PG_FUNCTION_ARGS)
 {
@@ -834,7 +896,10 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	Relation	seqrel;
 	Buffer		buf;
 	HeapTupleData seqtuple;
+	HeapTuple	tuple;
 	Form_pg_sequence seq;
+	Datum		amdata;
+	bool		amdata_isnull;
 
 	/* open and AccessShareLock sequence */
 	init_sequence(relid, &elm, &seqrel);
@@ -868,6 +933,21 @@ do_setval(Oid relid, int64 next, bool iscalled)
 						bufm, bufx)));
 	}
 
+	/* Get original amdata. */
+	amdata = fastgetattr(&seqtuple, SEQ_COL_AMDATA,
+						 RelationGetDescr(seqrel), &amdata_isnull);
+	if (amdata_isnull)
+		amdata = (Datum) 0;
+
+	/* Call into the sequence AM. */
+	amdata = sequence_setval(seqrel, seq->last_value, next, amdata);
+
+	/* Build new tuple with updated amdata and point seq to it. */
+	tuple = seqtup_update_amdata(&seqtuple, RelationGetDescr(seqrel), amdata);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
+
+	/* common logic we don't have to duplicate in every AM implementation */
+
 	/* Set the currval() state only if iscalled = true */
 	if (iscalled)
 	{
@@ -878,40 +958,12 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	/* In any case, forget any future cached numbers */
 	elm->cached = elm->last;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
 	seq->last_value = next;		/* last fetched number */
 	seq->is_called = iscalled;
 	seq->log_cnt = 0;
 
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		XLogRecData rdata[2];
-		Page		page = BufferGetPage(buf);
-
-		xlrec.node = seqrel->rd_node;
-		rdata[0].data = (char *) &xlrec;
-		rdata[0].len = sizeof(xl_seq_rec);
-		rdata[0].buffer = InvalidBuffer;
-		rdata[0].next = &(rdata[1]);
-
-		rdata[1].data = (char *) seqtuple.t_data;
-		rdata[1].len = seqtuple.t_len;
-		rdata[1].buffer = InvalidBuffer;
-		rdata[1].next = NULL;
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	/* ready to change the on-disk (or really, in-buffer) tuple */
+	replace_sequence_tuple(seqrel, buf, &seqtuple, tuple);
 
 	UnlockReleaseBuffer(buf);
 
@@ -1040,6 +1092,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 		elm->lxid = InvalidLocalTransactionId;
 		elm->last_valid = false;
 		elm->last = elm->cached = elm->increment = 0;
+		elm->amdata = PointerGetDatum(NULL);
 	}
 
 	/*
@@ -1130,15 +1183,15 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1148,13 +1201,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1399,7 +1452,7 @@ init_params(List *options, bool isInit,
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1415,8 +1468,7 @@ process_owned_by(Relation seqrel, List *owned_by)
 
 	nnames = list_length(owned_by);
 	Assert(nnames > 0);
-	if (nnames == 1)
-	{
+	if (nnames == 1)	{
 		/* Must be OWNED BY NONE */
 		if (strcmp(strVal(linitial(owned_by)), "none") != 0)
 			ereport(ERROR,
@@ -1548,6 +1600,134 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+		CatalogUpdateIndexes(rd, ctup);
+	}
+
+	heap_freetuple(ctup);
+	heap_close(rd, RowExclusiveLock);
+	CommandCounterIncrement();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tup, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+	XLogRecData rdata[2];
+
+	xlrec.node = seqrel->rd_node;
+	rdata[0].data = (char *) &xlrec;
+	rdata[0].len = sizeof(xl_seq_rec);
+	rdata[0].buffer = InvalidBuffer;
+	rdata[0].next = &(rdata[1]);
+
+	rdata[1].data = (char *) tup->t_data;
+	rdata[1].len = tup->t_len;
+	rdata[1].buffer = InvalidBuffer;
+	rdata[1].next = NULL;
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata);
+
+	PageSetLSN(page, recptr);
+}
+
+/*
+ * Replace the sequence tuple in a buffer and save it to the disk.
+ */
+static void
+replace_sequence_tuple(Relation seqrel, Buffer buf, HeapTuple tuple, HeapTuple newtup)
+{
+	Page temppage, page;
+
+	page = BufferGetPage(buf);
+
+	temppage = PageGetTempPageCopySpecial(page);
+
+	if (PageAddItem(temppage, (Item) newtup->t_data, newtup->t_len,
+					FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+		elog(PANIC, "replace_sequence_tuple: failed to add item to page");
+
+	PageSetLSN(temppage, PageGetLSN(page));
+
+	START_CRIT_SECTION();
+
+	PageRestoreTempPage(temppage, page);
+
+	MarkBufferDirty(buf);
+
+	tuple->t_len = newtup->t_len;
+
+	log_sequence_tuple(seqrel, tuple, page);
+
+	END_CRIT_SECTION();
+}
+
+/*
+ * Return copy of the sequence tuple with updated amdata.
+ */
+static HeapTuple
+seqtup_update_amdata(HeapTuple tuple, TupleDesc tupdesc, Datum amdata)
+{
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+	bool		repls[SEQ_COL_LASTCOL];
+	HeapTuple	newtup;
+
+	/* Build new tuple with updated amdata */
+	memset(values, 0, sizeof(values));
+	memset(nulls, false, sizeof(nulls));
+	memset(repls, false, sizeof(repls));
+
+	if (PointerIsValid(DatumGetPointer(amdata)))
+		values[SEQ_COL_AMDATA - 1] = amdata;
+	else
+		nulls[SEQ_COL_AMDATA - 1] = true;
+
+	repls[SEQ_COL_AMDATA - 1] = true;
+
+	newtup = heap_modify_tuple(tuple, tupdesc, values, nulls, repls);
+
+	/*
+	 * Sequence tuples must be frozen, because VACUUM does not examine
+	 * sequence table.
+	 */
+	HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+	HeapTupleHeaderSetXminFrozen(newtup->t_data);
+	HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+	HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+	newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+
+	return newtup;
+}
 
 void
 seq_redo(XLogRecPtr lsn, XLogRecord *record)
@@ -1602,6 +1782,7 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record)
 	pfree(localpage);
 }
 
+
 /*
  * Flush cached sequence information.
  */
@@ -1616,3 +1797,184 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+static Datum
+init_seqam(Oid oldAM, Oid *newAM, char *accessMethod, List *seqparams,
+		   List *reloptions, Datum amdata, bool isInit)
+{
+	Datum       reloptions_parsed;
+	Form_pg_seqam seqamForm;
+	HeapTuple   tuple = NULL;
+	char	   *validnsps[] = {NULL, NULL};
+	Datum		res;
+
+	if (oldAM && accessMethod == NULL)
+		*newAM = oldAM;
+	else if (accessMethod == NULL || strcmp(accessMethod, DEFAULT_SEQAM) == 0)
+		*newAM = GetDefaultSeqAM();
+	else
+		*newAM = get_seqam_oid(accessMethod, false);
+
+	tuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(*newAM));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", *newAM);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	/* First process/validate the standard sequence params */
+	res = sequence_seqparams(seqamForm->seqamseqparams, seqparams,
+							 amdata, isInit);
+
+	/* allow am specific options */
+	validnsps[0] = NameStr(seqamForm->seqamname);
+
+	/*
+	  *  Parse AM-specific options, convert to text array form,
+	  *  retrieve the AM-option function and then validate.
+	  */
+	reloptions_parsed = transformRelOptions((Datum) NULL, reloptions,
+											NULL, validnsps, false, false);
+
+	(void) sequence_reloptions(seqamForm->seqamreloptions, reloptions_parsed, true);
+
+	ReleaseSysCache(tuple);
+
+	return res;
+}
+
+
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64 next = *value;
+	int64 rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = LOCAL functions
+ *
+ *------------------------------------------------------------
+ */
+
+/*
+ * sequence_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+Datum
+sequence_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	int64	   *current_value = (int64 *) PG_GETARG_POINTER(1);
+	int64		fetch = PG_GETARG_INT64(2);
+	int64		min_value = PG_GETARG_INT64(3);
+	int64		max_value = PG_GETARG_INT64(4);
+	int64		increment_by = PG_GETARG_INT64(5);
+	bool		is_cycled = PG_GETARG_INT64(6);
+	int64	   *nallocated = (int64 *) PG_GETARG_POINTER(8);
+	int64		next,
+				rescnt = 0;
+
+	next = *current_value;
+	rescnt = sequence_increment(seqrel, &next, 1, min_value, max_value,
+								increment_by, is_cycled, true);
+	*current_value = next;
+	fetch--;
+
+	if (fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch, min_value, max_value,
+									 increment_by, is_cycled, false);
+
+	*nallocated = rescnt;
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * sequence_local_options()
+ *
+ * Verify the options of a local sequence.
+ */
+Datum
+sequence_local_options(PG_FUNCTION_ARGS)
+{
+	Datum       reloptions = PG_GETARG_DATUM(0);
+	bool        validate = PG_GETARG_BOOL(1);
+	bytea      *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+	PG_RETURN_NULL();
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ecdff1e..90f043a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9010,6 +9010,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 		case RELKIND_INDEX:
 			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
+		case RELKIND_SEQUENCE:
+			(void) sequence_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 21b070a..53b7fbd 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3330,7 +3330,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3343,7 +3345,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 358395f..3c37de9 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1566,7 +1566,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1577,7 +1579,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c98c27a..e8d0d5e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3519,7 +3520,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = DEFAULT_SEQAM;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3541,7 +3568,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3549,11 +3600,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3612,7 +3686,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 7c1939f..5994d18 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -399,6 +399,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 6351a9b..dfd3592 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -285,7 +285,10 @@ PageAddItem(Page page,
 	upper = (int) phdr->pd_upper - (int) alignedSize;
 
 	if (lower > upper)
+	{
+		elog(WARNING, "lower %d bigger than upper %d, offsetNumber %d", lower, upper, offsetNumber);
 		return InvalidOffsetNumber;
+	}
 
 	/*
 	 * OK to insert the item.  First, shuffle the existing pointers if needed.
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index eca3f97..0aa4cf2 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index c813779..1799cce 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -50,6 +50,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -264,6 +265,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1053,11 +1055,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1533,6 +1538,39 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for an index relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	MemoryContext	indexcxt;
+	Form_pg_seqam	amform;
+
+	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+									 RelationGetRelationName(rel),
+									 ALLOCSET_SMALL_MINSIZE,
+									 ALLOCSET_SMALL_INITSIZE,
+									 ALLOCSET_SMALL_MAXSIZE);
+	rel->rd_indexcxt = indexcxt;
+
+	rel->rd_aminfo = (RelationAmInfo *)
+		MemoryContextAllocZero(rel->rd_indexcxt,
+							   sizeof(RelationAmInfo));
+
+	/*
+	 * Make a copy of the pg_am entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+}
 
 /*
  *		formrdesc
@@ -4785,6 +4823,22 @@ load_relcache_init_file(bool shared)
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
+		else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+		{
+			MemoryContext indexcxt;
+			Assert(!rel->rd_isnailed);
+			Assert(false);
+
+			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+											 RelationGetRelationName(rel),
+											 ALLOCSET_SMALL_MINSIZE,
+											 ALLOCSET_SMALL_INITSIZE,
+											 ALLOCSET_SMALL_MAXSIZE);
+			rel->rd_indexcxt = indexcxt;
+			/* set up zeroed fmgr-info vectors */
+			rel->rd_aminfo = (RelationAmInfo *)
+				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+		}
 		else
 		{
 			/* Count nailed rels to ensure we have 'em all */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 94d951c..66deaa6 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAmNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_amname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAmOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 8111b93..5eba03b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2782,6 +2783,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"default_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence am for any new sequences."),
+			gettext_noop("An empty string selects the 'local' sequence am."),
+			GUC_IS_NAME | GUC_NOT_IN_SAMPLE
+		},
+		&default_seqam,
+		"",
+		check_default_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 1a9e82e..6f896aa 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14578,7 +14578,8 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = "local";
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14648,15 +14649,40 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	}
 #endif
 
-	startv = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
+	startv = pg_strdup(PQgetvalue(res, 0, 1));
+	incby = pg_strdup(PQgetvalue(res, 0, 2));
 	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
+		maxv = pg_strdup(PQgetvalue(res, 0, 3));
 	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
+		minv = pg_strdup(PQgetvalue(res, 0, 4));
+	cache = pg_strdup(PQgetvalue(res, 0, 5));
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
+	PQclear(res);
+
+	res = ExecuteSqlQuery(fout, "SELECT EXISTS(SELECT 1 "
+								"FROM pg_catalog.pg_class c, "
+								"pg_catalog.pg_namespace n "
+								"WHERE n.oid = c.relnamespace "
+								"AND c.relname = 'pg_seqam' "
+								"AND c.relkind = 'r');",
+						  PGRES_TUPLES_OK);
+	if (strcmp(PQgetvalue(res, 0, 0), "t") == 0)
+	{
+		PQclear(res);
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		amname = pg_strdup(PQgetvalue(res, 0, 0));
+	}
+
+	PQclear(res);
+
 	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
@@ -14698,6 +14724,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14764,8 +14791,6 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
-	PQclear(res);
-
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(delqry);
 	destroyPQExpBuffer(labelq);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 267f365..5df2b85 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1372,30 +1372,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data, false);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1439,6 +1415,8 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
 	res = PSQLexec(buf.data, false);
@@ -1446,6 +1424,39 @@ describeOneTableDetails(const char *schemaname,
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data, false);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1757,6 +1768,29 @@ describeOneTableDetails(const char *schemaname,
 		/* Footer information about a sequence */
 		PGresult   *result = NULL;
 
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data, false);
+
+		/*
+		 * If we get no rows back, don't show anything (obviously). We should
+		 * never get more than one row back, but if we do, just ignore it and
+		 * don't print anything.
+		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
+		PQclear(result);
+
 		/* Get the column that owns this sequence */
 		printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
 						  "\n   pg_catalog.quote_ident(relname) || '.' ||"
@@ -1774,6 +1808,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data, false);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1783,11 +1819,6 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
-		/*
-		 * If we get no rows back, don't show anything (obviously). We should
-		 * never get more than one row back, but if we do, just ignore it and
-		 * don't print anything.
-		 */
 		PQclear(result);
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index c226448..47ae485 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -45,8 +45,10 @@ typedef enum relopt_kind
 	RELOPT_KIND_TABLESPACE = (1 << 7),
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
+	RELOPT_KIND_SEQUENCE = (1 << 10),
+
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_VIEW,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -271,6 +273,8 @@ extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
 extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
 				 bool validate);
+extern bytea *sequence_reloptions(RegProcedure amoptions, Datum reloptions,
+				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..1664b74
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+extern char *default_seqam;
+
+extern Oid GetDefaultSeqAM(void);
+
+extern Datum sequence_alloc(Relation seqRelation, int64 *current_value,
+							int64 nrequested, int64 min_value,
+							int64 max_value, int64 increment_by,
+							bool is_cycled, Datum amdata,
+							int64 *nallocated, bool *xlog_needed);
+
+extern Datum sequence_setval(Relation seqRelation, int64 current_value,
+							 int64 new_value, Datum amdata);
+extern Datum sequence_update(Relation seqRelation, Datum amdata);
+extern Datum sequence_seqparams(RegProcedure amoptions, List *params,
+								Datum amdata, bool isInit);
+extern void sequence_request_update(Oid seqid);
+
+extern Datum sequence_local_alloc(PG_FUNCTION_ARGS);
+extern Datum sequence_local_options(PG_FUNCTION_ARGS);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 870692c..e4d585f 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAmNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAmOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 4736532..46f766f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4987,6 +4987,11 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6022 (  sequence_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 10 0 2281 "2281 2281 2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ sequence_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6024 (  sequence_local_options	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 2281 "2281 16" _null_ _null_ _null_ _null_ sequence_local_options _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 DESCR("list objects dropped by the current command");
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..f025e76
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	32
+
+CATALOG(pg_seqam,32)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamupdate;		/* update callback */
+	regproc		seqamseqparams;		/* process standard sequence params */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						6
+#define Anum_pg_seqam_amname				1
+#define Anum_pg_seqam_amalloc				2
+#define Anum_pg_seqam_amsetval				3
+#define Anum_pg_seqam_amupdate				4
+#define Anum_pg_seqam_amseqoptions			5
+#define Anum_pg_seqam_amreloptions			6
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 2 (  local		sequence_local_alloc - - - sequence_local_options));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 2
+
+#define DEFAULT_SEQAM	""
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 914d155..d6d0d78 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -31,6 +31,7 @@ typedef struct FormData_pg_sequence
 	int64		log_cnt;
 	bool		is_cycled;
 	bool		is_called;
+	bytea		amdata;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -49,9 +50,10 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 #define SEQ_COL_LOG				8
 #define SEQ_COL_CYCLE			9
 #define SEQ_COL_CALLED			10
+#define SEQ_COL_AMDATA			11
 
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_AMDATA
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index cef9544..e04c05d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2011,8 +2011,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2020,8 +2022,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 66b5cd3..68c7261 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -383,6 +383,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_default_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 198b98f..9d88062 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -49,10 +50,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
+ * functions, defined by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+	/* pg_am only */
 	FmgrInfo	aminsert;
 	FmgrInfo	ambeginscan;
 	FmgrInfo	amgettuple;
@@ -62,6 +65,16 @@ typedef struct RelationAmInfo
 	FmgrInfo	ammarkpos;
 	FmgrInfo	amrestrpos;
 	FmgrInfo	amcanreturn;
+	FmgrInfo	amcostestimate;
+
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+	FmgrInfo	seqamupdate;
+	FmgrInfo	seqamseqparams;
+
+	/* Common */
+	FmgrInfo	amoptions;
 } RelationAmInfo;
 
 
@@ -131,23 +144,25 @@ typedef struct RelationData
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+
 	/*
-	 * index access support info (used only for an index relation)
+	 * Access support info (used only for index or sequence relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
 	 * arrays are indexed by support function number, which is a sufficient
 	 * identifier given that restriction.
 	 *
-	 * Note: rd_amcache is available for index AMs to cache private data about
-	 * an index.  This must be just a cache since it may get reset at any time
+	 * Note: rd_amcache is available for AMs to cache private data about
+	 * an object.  This must be just a cache since it may get reset at any time
 	 * (in particular, it will get reset by a relcache inval message for the
 	 * index).  If used, it must point to a single memory chunk palloc'd in
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
-	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am */
+	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am or pg_seqam */
 	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
@@ -158,7 +173,7 @@ typedef struct RelationData
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
-	void	   *rd_amcache;		/* available for use by index AM */
+	void	   *rd_amcache;		/* available for use by AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
 
 	/*
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f97229f..2810a8d 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 2c8ec11..ddd96d5 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_range|t
 pg_rewrite|t
 pg_rowsecurity|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index a27b5fd..a6d5de5 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called | amdata 
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------+--------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f         | 
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called | amdata 
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------+--------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t         | 
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 80c5706..5e98b24 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -109,6 +109,7 @@ SELECT table_name, column_name, is_updatable
  ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
  ro_view19  | is_called     | NO
+ ro_view19  | amdata        | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
  ro_view20  | a             | NO
@@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable
  rw_view16  | a             | YES
  rw_view16  | b             | YES
  rw_view16  | aa            | YES
-(46 rows)
+(47 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
#26Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Petr Jelinek (#25)
Re: Sequence Access Method WIP

On 10/13/2014 01:01 PM, Petr Jelinek wrote:

Hi,

I rewrote the patch with different API along the lines of what was
discussed.

Thanks, that's better.

It would be good to see an alternative seqam to implement this API, to
see how it really works. The "local" one is too dummy to expose any
possible issues.

The API now consists of following functions:
sequence_alloc - allocating range of new values
The function receives the sequence relation, current value, number of
requested values amdata and relevant sequence options like min/max and
returns new amdata, new current value, number of values allocated and
also if it needs wal write (that should be returned if amdata has
changed plus other reasons the AM might have to force the wal update).

sequence_setval - notification that setval is happening
This function gets sequence relation, previous value and new value plus
the amdata and returns amdata (I can imagine some complex sequence AMs
will want to throw error that setval can't be done on them).

sequence_request_update/sequence_update - used for background processing
Basically AM can call the sequence_request_update and backend will then
call the sequence_update method of an AM with current amdata and will
write the updated amdata to disk

sequence_seqparams - function to process/validate the standard sequence
options like start position, min/max, increment by etc by the AM, it's
called in addition to the standard processing

sequence_reloptions - this is the only thing that remained unchanged
from previous patch, it's meant to pass custom options to the AM

Only the alloc and reloptions methods are required (and implemented by
the local AM).

The caching, xlog writing, updating the page, etc is handled by backend,
the AM does not see the tuple at all. I decided to not pass even the
struct around and just pass the relevant options because I think if we
want to abstract the storage properly then the AM should not care about
how the pg_sequence looks like at all, even if it means that the
sequence_alloc parameter list is bit long.

Hmm. The division of labour between the seqam and commands/sequence.c
still feels a bit funny. sequence.c keeps track of how many values have
been WAL-logged, and thus usable immediately, but we still call
sequence_alloc even when using up those already WAL-logged values. If
you think of using this for something like a centralized sequence server
in a replication cluster, you certainly don't want to make a call to the
remote server for every value - you'll want to cache them.

With the "local" seqam, there are two levels of caching. Each backend
caches some values (per the CACHE <value> option in CREATE SEQUENCE). In
addition to that, the server WAL-logs 32 values at a time. If you have a
remote seqam, it would most likely add a third cache, but it would
interact in strange ways with the second cache.

Considering a non-local seqam, the locking is also a bit strange. The
server keeps the sequence page locked throughout nextval(). But if the
actual state of the sequence is maintained elsewhere, there's no need to
serialize the calls to the remote allocator, i.e. the sequence_alloc()
calls.

I'm not exactly sure what to do about that. One option is to completely
move the maintenance of the "current" value, i.e. sequence.last_value,
to the seqam. That makes sense from an abstraction point of view. For
example with a remote server managing the sequence, storing the
"current" value in the local catalog table makes no sense as it's always
going to be out-of-date. The local seqam would store it as part of the
am-private data. However, you would need to move the responsibility of
locking and WAL-logging to the seqam. Maybe that's OK, but we'll need to
provide an API that the seqam can call to do that. Perhaps just let the
seqam call heap_inplace_update on the sequence relation.

For the amdata handling (which is the AM's private data variable) the
API assumes that (Datum) 0 is NULL, this seems to work well for
reloptions so should work here also and it simplifies things a little
compared to passing pointers to pointers around and making sure
everything is allocated, etc.

Sadly the fact that amdata is not fixed size and can be NULL made the
page updates of the sequence relation quite more complex that it used to
be.

It would be nice if the seqam could define exactly the columns it needs,
with any datatypes. There would be a set of common attributes:
sequence_name, start_value, cache_value, increment_by, max_value,
min_value, is_cycled. The local seqam would add "last_value", "log_cnt"
and "is_called" to that. A remote seqam that calls out to some other
server might store the remote server's hostname etc.

There could be a seqam function that returns a TupleDesc with the
required columns, for example.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#26)
Re: Sequence Access Method WIP

On 04/11/14 13:11, Heikki Linnakangas wrote:

On 10/13/2014 01:01 PM, Petr Jelinek wrote:

Hi,

I rewrote the patch with different API along the lines of what was
discussed.

Thanks, that's better.

It would be good to see an alternative seqam to implement this API, to
see how it really works. The "local" one is too dummy to expose any
possible issues.

Yeah, I don't know what that would be, It's hard for me to find
something that would sever purely demonstration purposes. I guess I
could port BDR sequences to this if it would help (once we have bit more
solid agreement that the proposed API at least theoretically seems ok so
that I don't have to rewrite it 10 times if at all possible).

...
Only the alloc and reloptions methods are required (and implemented by
the local AM).

The caching, xlog writing, updating the page, etc is handled by backend,
the AM does not see the tuple at all. I decided to not pass even the
struct around and just pass the relevant options because I think if we
want to abstract the storage properly then the AM should not care about
how the pg_sequence looks like at all, even if it means that the
sequence_alloc parameter list is bit long.

Hmm. The division of labour between the seqam and commands/sequence.c
still feels a bit funny. sequence.c keeps track of how many values have
been WAL-logged, and thus usable immediately, but we still call
sequence_alloc even when using up those already WAL-logged values. If
you think of using this for something like a centralized sequence server
in a replication cluster, you certainly don't want to make a call to the
remote server for every value - you'll want to cache them.

With the "local" seqam, there are two levels of caching. Each backend
caches some values (per the CACHE <value> option in CREATE SEQUENCE). In
addition to that, the server WAL-logs 32 values at a time. If you have a
remote seqam, it would most likely add a third cache, but it would
interact in strange ways with the second cache.

Considering a non-local seqam, the locking is also a bit strange. The
server keeps the sequence page locked throughout nextval(). But if the
actual state of the sequence is maintained elsewhere, there's no need to
serialize the calls to the remote allocator, i.e. the sequence_alloc()
calls.

I'm not exactly sure what to do about that. One option is to completely
move the maintenance of the "current" value, i.e. sequence.last_value,
to the seqam. That makes sense from an abstraction point of view. For
example with a remote server managing the sequence, storing the
"current" value in the local catalog table makes no sense as it's always
going to be out-of-date. The local seqam would store it as part of the
am-private data. However, you would need to move the responsibility of
locking and WAL-logging to the seqam. Maybe that's OK, but we'll need to
provide an API that the seqam can call to do that. Perhaps just let the
seqam call heap_inplace_update on the sequence relation.

My idea of how this works is - sequence_next handles the allocation and
the level2 caching (WAL logged cache) via amdata if it supports it, or
returns single value if it doesn't - then the WAL will always just write
the 1 value and there will basically be no level2 cache, since it is the
sequence_next who controls how much will be WAL-logged, what backend
asks for is just "suggestion".

The third level caching as you say should be solved by the
sequence_request_update and sequence_update callback - that will enable
the sequence AM to handle this kind of caching asynchronously without
blocking the sequence_next unnecessarily.

That way it's possible to implement many different strategies, from no
cache at all, to three levels of caching, with and without blocking of
calls to sequence_next.

For the amdata handling (which is the AM's private data variable) the
API assumes that (Datum) 0 is NULL, this seems to work well for
reloptions so should work here also and it simplifies things a little
compared to passing pointers to pointers around and making sure
everything is allocated, etc.

Sadly the fact that amdata is not fixed size and can be NULL made the
page updates of the sequence relation quite more complex that it used to
be.

It would be nice if the seqam could define exactly the columns it needs,
with any datatypes. There would be a set of common attributes:
sequence_name, start_value, cache_value, increment_by, max_value,
min_value, is_cycled. The local seqam would add "last_value", "log_cnt"
and "is_called" to that. A remote seqam that calls out to some other
server might store the remote server's hostname etc.

There could be a seqam function that returns a TupleDesc with the
required columns, for example.

Wouldn't that somewhat bloat catalog if we had new catalog table for
each sequence AM? It also does not really solve the amdata being dynamic
size "issue". I think just dynamic field where AM stores whatever it
wants is enough (ie the current solution), it's just that the hackery
that sequences storage implementation does is more visible once there
are non-fixed fields.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Petr Jelinek (#27)
Re: Sequence Access Method WIP

On 11/04/2014 11:01 PM, Petr Jelinek wrote:

On 04/11/14 13:11, Heikki Linnakangas wrote:

On 10/13/2014 01:01 PM, Petr Jelinek wrote:

Only the alloc and reloptions methods are required (and implemented by
the local AM).

The caching, xlog writing, updating the page, etc is handled by backend,
the AM does not see the tuple at all. I decided to not pass even the
struct around and just pass the relevant options because I think if we
want to abstract the storage properly then the AM should not care about
how the pg_sequence looks like at all, even if it means that the
sequence_alloc parameter list is bit long.

Hmm. The division of labour between the seqam and commands/sequence.c
still feels a bit funny. sequence.c keeps track of how many values have
been WAL-logged, and thus usable immediately, but we still call
sequence_alloc even when using up those already WAL-logged values. If
you think of using this for something like a centralized sequence server
in a replication cluster, you certainly don't want to make a call to the
remote server for every value - you'll want to cache them.

With the "local" seqam, there are two levels of caching. Each backend
caches some values (per the CACHE <value> option in CREATE SEQUENCE). In
addition to that, the server WAL-logs 32 values at a time. If you have a
remote seqam, it would most likely add a third cache, but it would
interact in strange ways with the second cache.

Considering a non-local seqam, the locking is also a bit strange. The
server keeps the sequence page locked throughout nextval(). But if the
actual state of the sequence is maintained elsewhere, there's no need to
serialize the calls to the remote allocator, i.e. the sequence_alloc()
calls.

I'm not exactly sure what to do about that. One option is to completely
move the maintenance of the "current" value, i.e. sequence.last_value,
to the seqam. That makes sense from an abstraction point of view. For
example with a remote server managing the sequence, storing the
"current" value in the local catalog table makes no sense as it's always
going to be out-of-date. The local seqam would store it as part of the
am-private data. However, you would need to move the responsibility of
locking and WAL-logging to the seqam. Maybe that's OK, but we'll need to
provide an API that the seqam can call to do that. Perhaps just let the
seqam call heap_inplace_update on the sequence relation.

My idea of how this works is - sequence_next handles the allocation and
the level2 caching (WAL logged cache) via amdata if it supports it, or
returns single value if it doesn't - then the WAL will always just write
the 1 value and there will basically be no level2 cache, since it is the
sequence_next who controls how much will be WAL-logged, what backend
asks for is just "suggestion".

Hmm, so the AM might return an "nallocated" value less than the "fetch"
value that sequence.c requested? As the patch stands, wouldn't that make
sequence.c write a WAL record more often?

In fact, if the seqam manages the current value outside the database
(e.g. a "remote" seqam that gets the value from another server),
nextval() never needs to write a WAL record.

The third level caching as you say should be solved by the
sequence_request_update and sequence_update callback - that will enable
the sequence AM to handle this kind of caching asynchronously without
blocking the sequence_next unnecessarily.

That way it's possible to implement many different strategies, from no
cache at all, to three levels of caching, with and without blocking of
calls to sequence_next.

It's nice that asynchronous operation is possible, but that seems like a
more advanced feature. Surely an approach where you fetch a bunch of
values when needed is going to be more common. Let's focus on the simple
synchronous case first, and make sure the API works well for that.

For the amdata handling (which is the AM's private data variable) the
API assumes that (Datum) 0 is NULL, this seems to work well for
reloptions so should work here also and it simplifies things a little
compared to passing pointers to pointers around and making sure
everything is allocated, etc.

Sadly the fact that amdata is not fixed size and can be NULL made the
page updates of the sequence relation quite more complex that it used to
be.

It would be nice if the seqam could define exactly the columns it needs,
with any datatypes. There would be a set of common attributes:
sequence_name, start_value, cache_value, increment_by, max_value,
min_value, is_cycled. The local seqam would add "last_value", "log_cnt"
and "is_called" to that. A remote seqam that calls out to some other
server might store the remote server's hostname etc.

There could be a seqam function that returns a TupleDesc with the
required columns, for example.

Wouldn't that somewhat bloat catalog if we had new catalog table for
each sequence AM?

No, that's not what I meant. The number of catalog tables would be the
same as today. Sequences look much like any other relation, with entries
in pg_attribute catalog table for all the attributes for each sequence.
Currently, all sequences have the same set of attributes, sequence_name,
last_value and so forth. What I'm proposing is that there would a set of
attributes that are common to all sequences, but in addition to that
there could be any number of AM-specific attributes.

It also does not really solve the amdata being dynamic
size "issue".

Yes it would. There would not be a single amdata attribute, but the AM
could specify any number of custom attributes, which could be fixed size
or varlen. It would be solely the AM's responsibility to set the values
of those attributes.

I think just dynamic field where AM stores whatever it
wants is enough (ie the current solution), it's just that the hackery
that sequences storage implementation does is more visible once there
are non-fixed fields.

Using a single amdata field is like creating a table with a single text
column, and putting all your data in the single column separated by
tabs. I'm sure you can make it work, but normalized data is much nicer
to work with.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#28)
Re: Sequence Access Method WIP

On 05/11/14 13:45, Heikki Linnakangas wrote:

On 11/04/2014 11:01 PM, Petr Jelinek wrote:

On 04/11/14 13:11, Heikki Linnakangas wrote:

On 10/13/2014 01:01 PM, Petr Jelinek wrote:

Only the alloc and reloptions methods are required (and implemented by
the local AM).

The caching, xlog writing, updating the page, etc is handled by
backend,
the AM does not see the tuple at all. I decided to not pass even the
struct around and just pass the relevant options because I think if we
want to abstract the storage properly then the AM should not care about
how the pg_sequence looks like at all, even if it means that the
sequence_alloc parameter list is bit long.

Hmm. The division of labour between the seqam and commands/sequence.c
still feels a bit funny. sequence.c keeps track of how many values have
been WAL-logged, and thus usable immediately, but we still call
sequence_alloc even when using up those already WAL-logged values. If
you think of using this for something like a centralized sequence server
in a replication cluster, you certainly don't want to make a call to the
remote server for every value - you'll want to cache them.

With the "local" seqam, there are two levels of caching. Each backend
caches some values (per the CACHE <value> option in CREATE SEQUENCE). In
addition to that, the server WAL-logs 32 values at a time. If you have a
remote seqam, it would most likely add a third cache, but it would
interact in strange ways with the second cache.

Considering a non-local seqam, the locking is also a bit strange. The
server keeps the sequence page locked throughout nextval(). But if the
actual state of the sequence is maintained elsewhere, there's no need to
serialize the calls to the remote allocator, i.e. the sequence_alloc()
calls.

I'm not exactly sure what to do about that. One option is to completely
move the maintenance of the "current" value, i.e. sequence.last_value,
to the seqam. That makes sense from an abstraction point of view. For
example with a remote server managing the sequence, storing the
"current" value in the local catalog table makes no sense as it's always
going to be out-of-date. The local seqam would store it as part of the
am-private data. However, you would need to move the responsibility of
locking and WAL-logging to the seqam. Maybe that's OK, but we'll need to
provide an API that the seqam can call to do that. Perhaps just let the
seqam call heap_inplace_update on the sequence relation.

My idea of how this works is - sequence_next handles the allocation and
the level2 caching (WAL logged cache) via amdata if it supports it, or
returns single value if it doesn't - then the WAL will always just write
the 1 value and there will basically be no level2 cache, since it is the
sequence_next who controls how much will be WAL-logged, what backend
asks for is just "suggestion".

Hmm, so the AM might return an "nallocated" value less than the "fetch"
value that sequence.c requested? As the patch stands, wouldn't that make
sequence.c write a WAL record more often?

That's correct, that's also why you usually want to have some form of
local caching if possible.

In fact, if the seqam manages the current value outside the database
(e.g. a "remote" seqam that gets the value from another server),
nextval() never needs to write a WAL record.

Sure it does, you need to keep the current state in Postgres also, at
least the current value so that you can pass correct input to
sequence_alloc(). And you need to do this in crash-safe way so WAL is
necessary.

I think sequences will cache in amdata if possible for that type of
sequence and in case where it's not and it's caching on some external
server then you'll most likely get bigger overhead from network than WAL
anyway...

For the amdata handling (which is the AM's private data variable) the
API assumes that (Datum) 0 is NULL, this seems to work well for
reloptions so should work here also and it simplifies things a little
compared to passing pointers to pointers around and making sure
everything is allocated, etc.

Sadly the fact that amdata is not fixed size and can be NULL made the
page updates of the sequence relation quite more complex that it
used to
be.

It would be nice if the seqam could define exactly the columns it needs,
with any datatypes. There would be a set of common attributes:
sequence_name, start_value, cache_value, increment_by, max_value,
min_value, is_cycled. The local seqam would add "last_value", "log_cnt"
and "is_called" to that. A remote seqam that calls out to some other
server might store the remote server's hostname etc.

There could be a seqam function that returns a TupleDesc with the
required columns, for example.

Wouldn't that somewhat bloat catalog if we had new catalog table for
each sequence AM?

No, that's not what I meant. The number of catalog tables would be the
same as today. Sequences look much like any other relation, with entries
in pg_attribute catalog table for all the attributes for each sequence.
Currently, all sequences have the same set of attributes, sequence_name,
last_value and so forth. What I'm proposing is that there would a set of
attributes that are common to all sequences, but in addition to that
there could be any number of AM-specific attributes.

Oh, that's interesting idea, so the AM interfaces would basically return
updated tuple and there would be some description function that returns
tupledesc. I am bit worried that this would kill any possibility of
ALTER SEQUENCE USING access_method. Plus I don't think it actually
solves any real problem - serializing the internal C structs into bytea
is not any harder than serializing them into tuple IMHO.

It also does not really solve the amdata being dynamic
size "issue".

Yes it would. There would not be a single amdata attribute, but the AM
could specify any number of custom attributes, which could be fixed size
or varlen. It would be solely the AM's responsibility to set the values
of those attributes.

That's not the issue I was referring to, I was talking about the page
replacement code which is not as simple now that we have potentially
dynamic size tuple and if tuples were different for different AMs the
code would still have to be able to handle that case. Setting the values
in tuple itself is not too complicated.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Petr Jelinek (#29)
Re: Sequence Access Method WIP

On 11/05/2014 05:07 PM, Petr Jelinek wrote:

On 05/11/14 13:45, Heikki Linnakangas wrote:

In fact, if the seqam manages the current value outside the database
(e.g. a "remote" seqam that gets the value from another server),
nextval() never needs to write a WAL record.

Sure it does, you need to keep the current state in Postgres also, at
least the current value so that you can pass correct input to
sequence_alloc(). And you need to do this in crash-safe way so WAL is
necessary.

Why does sequence_alloc need the current value? If it's a "remote"
seqam, the current value is kept in the remote server, and the last
value that was given to this PostgreSQL server is irrelevant.

That irks me with this API. The method for acquiring a new value isn't
fully abstracted behind the AM interface, as sequence.c still needs to
track it itself. That's useful for the local AM, of course, and maybe
some others, but for others it's totally useless.

For the amdata handling (which is the AM's private data variable) the
API assumes that (Datum) 0 is NULL, this seems to work well for
reloptions so should work here also and it simplifies things a little
compared to passing pointers to pointers around and making sure
everything is allocated, etc.

Sadly the fact that amdata is not fixed size and can be NULL made the
page updates of the sequence relation quite more complex that it
used to
be.

It would be nice if the seqam could define exactly the columns it needs,
with any datatypes. There would be a set of common attributes:
sequence_name, start_value, cache_value, increment_by, max_value,
min_value, is_cycled. The local seqam would add "last_value", "log_cnt"
and "is_called" to that. A remote seqam that calls out to some other
server might store the remote server's hostname etc.

There could be a seqam function that returns a TupleDesc with the
required columns, for example.

Wouldn't that somewhat bloat catalog if we had new catalog table for
each sequence AM?

No, that's not what I meant. The number of catalog tables would be the
same as today. Sequences look much like any other relation, with entries
in pg_attribute catalog table for all the attributes for each sequence.
Currently, all sequences have the same set of attributes, sequence_name,
last_value and so forth. What I'm proposing is that there would a set of
attributes that are common to all sequences, but in addition to that
there could be any number of AM-specific attributes.

Oh, that's interesting idea, so the AM interfaces would basically return
updated tuple and there would be some description function that returns
tupledesc.

Yeah, something like that.

I am bit worried that this would kill any possibility of
ALTER SEQUENCE USING access_method. Plus I don't think it actually
solves any real problem - serializing the internal C structs into bytea
is not any harder than serializing them into tuple IMHO.

I agree that serialization to bytea isn't that difficult, but it's still
nicer to work directly with the correct data types. And it makes the
internal state easily accessible for monitoring and debugging purposes.

It also does not really solve the amdata being dynamic
size "issue".

Yes it would. There would not be a single amdata attribute, but the AM
could specify any number of custom attributes, which could be fixed size
or varlen. It would be solely the AM's responsibility to set the values
of those attributes.

That's not the issue I was referring to, I was talking about the page
replacement code which is not as simple now that we have potentially
dynamic size tuple and if tuples were different for different AMs the
code would still have to be able to handle that case. Setting the values
in tuple itself is not too complicated.

I don't see the problem with that. We deal with variable-sized tuples in
heap pages all the time. The max size of amdata (or the extra
AM-specific columns) is going to be determined by the block size, though.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#30)
Re: Sequence Access Method WIP

On 05/11/14 18:32, Heikki Linnakangas wrote:

On 11/05/2014 05:07 PM, Petr Jelinek wrote:

On 05/11/14 13:45, Heikki Linnakangas wrote:

In fact, if the seqam manages the current value outside the database
(e.g. a "remote" seqam that gets the value from another server),
nextval() never needs to write a WAL record.

Sure it does, you need to keep the current state in Postgres also, at
least the current value so that you can pass correct input to
sequence_alloc(). And you need to do this in crash-safe way so WAL is
necessary.

Why does sequence_alloc need the current value? If it's a "remote"
seqam, the current value is kept in the remote server, and the last
value that was given to this PostgreSQL server is irrelevant.

Hmm, I am not sure if I see this usecase as practical TBH, but I also
don't see fundamental problem with it.

That irks me with this API. The method for acquiring a new value isn't
fully abstracted behind the AM interface, as sequence.c still needs to
track it itself. That's useful for the local AM, of course, and maybe
some others, but for others it's totally useless.

Hmm, I think that kind of abstraction can only be done by passing
current tuple and returning updated tuple (yes I realize that it's what
you have been saying basically).

In general it sounds like the level of abstraction you'd want would be
one where AM cares about everything except the the code that does the
actual writes to page and WAL (but when to do those would still be
controlled completely by AM?) and the SQL interface.
I don't see how to make that work with ALTER SEQUENCE USING to be honest
and I do care quite a lot about that use-case (I think the ability to
convert the "local" sequences to 3rd party ones and back is very important).

That's not the issue I was referring to, I was talking about the page
replacement code which is not as simple now that we have potentially
dynamic size tuple and if tuples were different for different AMs the
code would still have to be able to handle that case. Setting the values
in tuple itself is not too complicated.

I don't see the problem with that. We deal with variable-sized tuples in
heap pages all the time. The max size of amdata (or the extra
AM-specific columns) is going to be determined by the block size, though.

Glad to hear that. Yes the limit is block size, I think we can live with
that at least for the moment...

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Craig Ringer
craig@2ndquadrant.com
In reply to: Petr Jelinek (#27)
Re: Sequence Access Method WIP

On 11/05/2014 05:01 AM, Petr Jelinek wrote:

I guess I could port BDR sequences to this if it would help (once we
have bit more solid agreement that the proposed API at least
theoretically seems ok so that I don't have to rewrite it 10 times if at
all possible).

Because the BDR sequences rely on all the other BDR machinery I suspect
that'd be a pretty big thing to review and follow for someone who
doesn't know the BDR code.

Do you think it'd be simple to provide a blocking, transactional
sequence allocator via this API? i.e. gapless sequences, much the same
as typically implemented with SELECT ... FOR UPDATE on a counter table.

It might be more digestible standalone, and would be a handy contrib/
example extension demonstrating use of the API.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Craig Ringer (#32)
Re: Sequence Access Method WIP

Craig Ringer wrote:

On 11/05/2014 05:01 AM, Petr Jelinek wrote:

I guess I could port BDR sequences to this if it would help (once we
have bit more solid agreement that the proposed API at least
theoretically seems ok so that I don't have to rewrite it 10 times if at
all possible).

Because the BDR sequences rely on all the other BDR machinery I suspect
that'd be a pretty big thing to review and follow for someone who
doesn't know the BDR code.

Do you think it'd be simple to provide a blocking, transactional
sequence allocator via this API? i.e. gapless sequences, much the same
as typically implemented with SELECT ... FOR UPDATE on a counter table.

I think that would be a useful contrib module too; gapless allocation is
a frequent user need. If we can build it in some reasonable way with the
seqam API, it'd show the API is good.

But on the other hand, since BDR sequences are the ultimate goal of this
patch, we need to make sure that they can be made to work with whatever
the seqam API ends up being. It's not necessary for reviewers here to
review that code, but the BDR developers need to say "yes, this API
works for us" -- just like the slony developers (well, ssinger) did for
logical decoding.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Petr Jelinek
petr@2ndquadrant.com
In reply to: Craig Ringer (#32)
Re: Sequence Access Method WIP

On 06/11/14 11:22, Craig Ringer wrote:

On 11/05/2014 05:01 AM, Petr Jelinek wrote:

I guess I could port BDR sequences to this if it would help (once we
have bit more solid agreement that the proposed API at least
theoretically seems ok so that I don't have to rewrite it 10 times if at
all possible).

Because the BDR sequences rely on all the other BDR machinery I suspect
that'd be a pretty big thing to review and follow for someone who
doesn't know the BDR code.

Do you think it'd be simple to provide a blocking, transactional
sequence allocator via this API? i.e. gapless sequences, much the same
as typically implemented with SELECT ... FOR UPDATE on a counter table.

It might be more digestible standalone, and would be a handy contrib/
example extension demonstrating use of the API.

Yes I think that's doable (once we iron out the API we can agree on).

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Robert Haas
robertmhaas@gmail.com
In reply to: Petr Jelinek (#31)
Re: Sequence Access Method WIP

On Nov 5, 2014, at 5:43 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

I don't see how to make that work with ALTER SEQUENCE USING to be honest and I do care quite a lot about that use-case (I think the ability to convert the "local" sequences to 3rd party ones and back is very important).

What specific problems do you foresee? There's an issue if something depends on one of the added sequence columns, but if that is the case then you had *better* fail.

I think that the debugability value of making extra sequence columns human-readable is quite high.

...Robert

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Petr Jelinek
petr@2ndquadrant.com
In reply to: Robert Haas (#35)
Re: Sequence Access Method WIP

On 08/11/14 00:45, Robert Haas wrote:

On Nov 5, 2014, at 5:43 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

I don't see how to make that work with ALTER SEQUENCE USING to be honest and I do care quite a lot about that use-case (I think the ability to convert the "local" sequences to 3rd party ones and back is very important).

What specific problems do you foresee? There's an issue if something depends on one of the added sequence columns, but if that is the case then you had *better* fail.

I think that the debugability value of making extra sequence columns human-readable is quite high.

My main problem is actually not with having tuple per-seqAM, but more
with the fact that Heikki does not want to have last_value as compulsory
column/parameter. How is the new AM then supposed to know where to pick
up and if it even can pick up?

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Petr Jelinek
petr@2ndquadrant.com
In reply to: Petr Jelinek (#36)
Re: Sequence Access Method WIP

On 08/11/14 00:57, Petr Jelinek wrote:

On 08/11/14 00:45, Robert Haas wrote:

On Nov 5, 2014, at 5:43 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

I don't see how to make that work with ALTER SEQUENCE USING to be
honest and I do care quite a lot about that use-case (I think the
ability to convert the "local" sequences to 3rd party ones and back
is very important).

What specific problems do you foresee? There's an issue if something
depends on one of the added sequence columns, but if that is the case
then you had *better* fail.

I think that the debugability value of making extra sequence columns
human-readable is quite high.

My main problem is actually not with having tuple per-seqAM, but more
with the fact that Heikki does not want to have last_value as compulsory
column/parameter. How is the new AM then supposed to know where to pick
up and if it even can pick up?

And obviously, once the last_value is part of the compulsory columns we
again have to WAL log all the time for the use-case which Heikki is
using as model, so it does not help there (just to clear what my point
was about).

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#38Robert Haas
robertmhaas@gmail.com
In reply to: Petr Jelinek (#37)
Re: Sequence Access Method WIP

On Fri, Nov 7, 2014 at 7:26 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

My main problem is actually not with having tuple per-seqAM, but more
with the fact that Heikki does not want to have last_value as compulsory
column/parameter. How is the new AM then supposed to know where to pick
up and if it even can pick up?

That seems pretty well impossible to know anyway. If the pluggable AM
was handing out values at random or in some unpredictable fashion,
there may be no well-defined point where it's safe for the default AM
to resume. Granted, in the case of replication, it probably is
possible, and maybe that's important enough to be worth catering to.

And obviously, once the last_value is part of the compulsory columns we
again have to WAL log all the time for the use-case which Heikki is using as
model, so it does not help there (just to clear what my point was about).

But I don't know what to do about that. :-(

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39Craig Ringer
craig@2ndquadrant.com
In reply to: Petr Jelinek (#34)
Re: Sequence Access Method WIP

On 11/08/2014 12:35 AM, Petr Jelinek wrote:

Do you think it'd be simple to provide a blocking, transactional
sequence allocator via this API? i.e. gapless sequences, much the same
as typically implemented with SELECT ... FOR UPDATE on a counter table.

It might be more digestible standalone, and would be a handy contrib/
example extension demonstrating use of the API.

Yes I think that's doable (once we iron out the API we can agree on).

Cool; I think that'd be an interesting self-contained example that'd
also have real-world uses ... and provide a nice succinct way to answer
those semi-regular "how do I create a sequence that doesn't have holes"
questions.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Petr Jelinek
petr@2ndquadrant.com
In reply to: Robert Haas (#38)
Re: Sequence Access Method WIP

On 08/11/14 03:10, Robert Haas wrote:

On Fri, Nov 7, 2014 at 7:26 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

My main problem is actually not with having tuple per-seqAM, but more
with the fact that Heikki does not want to have last_value as compulsory
column/parameter. How is the new AM then supposed to know where to pick
up and if it even can pick up?

That seems pretty well impossible to know anyway. If the pluggable AM
was handing out values at random or in some unpredictable fashion,
there may be no well-defined point where it's safe for the default AM
to resume. Granted, in the case of replication, it probably is
possible, and maybe that's important enough to be worth catering to.

While this is true, I think 99% of this use-case in practice is about
converting existing schema with "local" sequence to some other sequence
and perhaps fixing schema after people create sequence using wrong AM
because their default is not what they thought it is.

And obviously, once the last_value is part of the compulsory columns we
again have to WAL log all the time for the use-case which Heikki is using as
model, so it does not help there (just to clear what my point was about).

But I don't know what to do about that. :-(

Me neither and I don't think this is actually solvable, we either have
last_value and logcnt as mandatory columns and the central sequence
server example Heikki is talking about will be impacted by this, or we
leave those columns to AM implementations and we lose the ability to do
AM change for majority of cases, and also IMHO most AMs will then have
to implement their own last_value and logcnt logic anyway.

Honestly, I am still not convinced that the centralized sequence server
with no local caching is something we should be optimizing for as that
one will suffer from performance problems anyway. And it can ignore the
last_value input from postgres if it choses to, so it's not like the
currently proposed patch forbids implementation of such AMs.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Simon Riggs
simon@2ndQuadrant.com
In reply to: Heikki Linnakangas (#30)
Re: Sequence Access Method WIP

On 5 November 2014 17:32, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

Why does sequence_alloc need the current value? If it's a "remote" seqam,
the current value is kept in the remote server, and the last value that was
given to this PostgreSQL server is irrelevant.

That irks me with this API. The method for acquiring a new value isn't fully
abstracted behind the AM interface, as sequence.c still needs to track it
itself. That's useful for the local AM, of course, and maybe some others,
but for others it's totally useless.

Please bear in mind what seqam is for...

At present it's only use is to provide Global Sequences. There's a few
ways of doing that, but they all look sorta similar.

The only other use we thought of was shot down, so producing the
perfect API isn't likely to help anyone. It's really not worth the
effort to produce a better API.

BDR doesn't require Global Sequences, nor are Global Sequences
restricted in their use to just BDR - lots of cluster configurations
would want something like this.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Robert Haas
robertmhaas@gmail.com
In reply to: Petr Jelinek (#40)
Re: Sequence Access Method WIP

On Sat, Nov 8, 2014 at 6:17 AM, Petr Jelinek <petr@2ndquadrant.com> wrote:

Honestly, I am still not convinced that the centralized sequence server with
no local caching is something we should be optimizing for as that one will
suffer from performance problems anyway. And it can ignore the last_value
input from postgres if it choses to, so it's not like the currently proposed
patch forbids implementation of such AMs.

I can buy that.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Petr Jelinek (#36)
Re: Sequence Access Method WIP

On 11/08/2014 01:57 AM, Petr Jelinek wrote:

My main problem is actually not with having tuple per-seqAM, but more
with the fact that Heikki does not want to have last_value as compulsory
column/parameter. How is the new AM then supposed to know where to pick
up and if it even can pick up?

Call nextval(), and use the value it returns as the starting point for
the new AM.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#43)
Re: Sequence Access Method WIP

On 09/11/14 20:47, Heikki Linnakangas wrote:

On 11/08/2014 01:57 AM, Petr Jelinek wrote:

My main problem is actually not with having tuple per-seqAM, but more
with the fact that Heikki does not want to have last_value as compulsory
column/parameter. How is the new AM then supposed to know where to pick
up and if it even can pick up?

Call nextval(), and use the value it returns as the starting point for
the new AM.

Hah, so simple, works for me.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#45Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Simon Riggs (#41)
Re: Sequence Access Method WIP

On 11/08/2014 04:21 PM, Simon Riggs wrote:

On 5 November 2014 17:32, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

Why does sequence_alloc need the current value? If it's a "remote" seqam,
the current value is kept in the remote server, and the last value that was
given to this PostgreSQL server is irrelevant.

That irks me with this API. The method for acquiring a new value isn't fully
abstracted behind the AM interface, as sequence.c still needs to track it
itself. That's useful for the local AM, of course, and maybe some others,
but for others it's totally useless.

Please bear in mind what seqam is for...

At present it's only use is to provide Global Sequences. There's a few
ways of doing that, but they all look sorta similar.

Ok.

The only other use we thought of was shot down, so producing the
perfect API isn't likely to help anyone. It's really not worth the
effort to produce a better API.

Your argument seems to be that because this API is only used for
creating global sequences, it's not worth it to make it better for that
purpose. That doesn't make much sense, so that's probably not what you
meant. I'm confused.

To be clear: I don't think this API is very good for its stated purpose,
for implementing global sequences for use in a cluster. For the reasons
I've mentioned before. I'd like to see two changes to this proposal:

1. Make the AM implementation solely responsible for remembering the
"last value". (if it's a global or remote sequence, the current value
might not be stored in the local server at all)

2. Instead of the single amdata field, make it possible for the
implementation to define any number of fields with any datatype in the
tuple. That would make debugging, monitoring etc. easier.

BDR doesn't require Global Sequences, nor are Global Sequences
restricted in their use to just BDR - lots of cluster configurations
would want something like this.

Sure. (I don't see how that's relevant to this discussion, however).

(marking this as "Returned with Feedback" in the commitfest)

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#46Andres Freund
andres@2ndquadrant.com
In reply to: Heikki Linnakangas (#45)
Re: Sequence Access Method WIP

On 2014-11-24 13:16:24 +0200, Heikki Linnakangas wrote:

To be clear: I don't think this API is very good for its stated purpose, for
implementing global sequences for use in a cluster. For the reasons I've
mentioned before. I'd like to see two changes to this proposal:

1. Make the AM implementation solely responsible for remembering the "last
value". (if it's a global or remote sequence, the current value might not be
stored in the local server at all)

I think that reason isn't particularly good. The practical applicability
for such a implementation doesn't seem to be particularly large.

2. Instead of the single amdata field, make it possible for the
implementation to define any number of fields with any datatype in the
tuple. That would make debugging, monitoring etc. easier.

My main problem with that approach is that it pretty much nails the door
shut for moving sequences into a catalog table instead of the current,
pretty insane, approach of a physical file per sequence. Currently, with
our without seqam, it'd not be all that hard to force it into a catalog,
taking care to to force each tuple onto a separate page...

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#47José Luis Tallón
jltallon@adv-solutions.net
In reply to: Andres Freund (#46)
Re: Sequence Access Method WIP

On 12/02/2014 08:21 PM, Andres Freund wrote:

[snip]

2. Instead of the single amdata field, make it possible for the
implementation to define any number of fields with any datatype in the
tuple. That would make debugging, monitoring etc. easier.

My main problem with that approach is that it pretty much nails the door
shut for moving sequences into a catalog table instead of the current,
pretty insane, approach of a physical file per sequence.

Hmm... having done my fair bit of testing, I can say that this isn't
actually that bad (though depends heavily on the underlying filesystem
and workload, of course)
With this approach, I fear extreme I/O contention with an update-heavy
workload... unless all sequence activity is finally WAL-logged and hence
writes to the actual files become mostly sequential and asynchronous.

May I possibly suggest a file-per-schema model instead? This approach
would certainly solve the excessive i-node consumption problem that --I
guess-- Andres is trying to address here.
Moreover, the "one file per schema for sequences" solution would fit a
quite common model of grouping tables (in schemas) for physical
[tablespace] location purposes....

Currently, with
our without seqam, it'd not be all that hard to force it into a catalog,
taking care to to force each tuple onto a separate page...

IMHO, this is jst as wasteful as the current approach (one-page file per
sequence) in terms of disk usage and complicates the code a bit .... but
I really don't see how we can have more than one sequence state per page
without severe (page) locking problems.
However, someone with deeper knowledge of page pinning and buffer
manager internals could certainly devise a better solution...

Just my 2c

Thanks,

/ J.L.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#48Petr Jelinek
petr.jelinek@2ndquadrant.com
In reply to: Andres Freund (#46)
Re: Sequence Access Method WIP

On 02/12/14 20:21, Andres Freund wrote:

On 2014-11-24 13:16:24 +0200, Heikki Linnakangas wrote:

To be clear: I don't think this API is very good for its stated purpose, for
implementing global sequences for use in a cluster. For the reasons I've
mentioned before. I'd like to see two changes to this proposal:
...
2. Instead of the single amdata field, make it possible for the
implementation to define any number of fields with any datatype in the
tuple. That would make debugging, monitoring etc. easier.

My main problem with that approach is that it pretty much nails the door
shut for moving sequences into a catalog table instead of the current,
pretty insane, approach of a physical file per sequence. Currently, with
our without seqam, it'd not be all that hard to force it into a catalog,
taking care to to force each tuple onto a separate page...

I don't know, I think if we decide to change storage format we can do
serialization/conversion in seqam layer, it does not seem to matter too
much if the serialization into some opaque type is done in AM itself or
by the API layer. Or we can have one relation for all sequences in
single AM, etc. In general I don't think that the custom columns for AM
approach prohibits future storage changes.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#49Andres Freund
andres@2ndquadrant.com
In reply to: José Luis Tallón (#47)
Re: Sequence Access Method WIP

On 2014-12-03 10:59:50 +0100, Jos� Luis Tall�n wrote:

On 12/02/2014 08:21 PM, Andres Freund wrote:

[snip]

2. Instead of the single amdata field, make it possible for the
implementation to define any number of fields with any datatype in the
tuple. That would make debugging, monitoring etc. easier.

My main problem with that approach is that it pretty much nails the door
shut for moving sequences into a catalog table instead of the current,
pretty insane, approach of a physical file per sequence.

Hmm... having done my fair bit of testing, I can say that this isn't
actually that bad (though depends heavily on the underlying filesystem and
workload, of course)
With this approach, I fear extreme I/O contention with an update-heavy
workload... unless all sequence activity is finally WAL-logged and hence
writes to the actual files become mostly sequential and asynchronous.

I don't think the WAL logging would need to change much in comparison to
the current solution. We'd just add the page number to the WAL record.

The biggest advantage would be to require fewer heavyweight locks,
because the pg_sequence one could be a single fastpath lock. Currently
we have to take the sequence's relation lock (heavyweight) and then the
the page level lock (lwlock) for every single sequence used.

May I possibly suggest a file-per-schema model instead? This approach would
certainly solve the excessive i-node consumption problem that --I guess--
Andres is trying to address here.

I don't think that really has any advantages.

Currently, with
our without seqam, it'd not be all that hard to force it into a catalog,
taking care to to force each tuple onto a separate page...

IMHO, this is jst as wasteful as the current approach (one-page file per
sequence) in terms of disk usage and complicates the code a bit .... but I
really don't see how we can have more than one sequence state per page
without severe (page) locking problems.

The overhead of a file is much more than wasting the remainder of a
page. Alone the requirement of separate fsyncs and everything is pretty
bothersome. The generated IO patterns are also much worse...

However, someone with deeper knowledge of page pinning and buffer manager
internals could certainly devise a better solution...

I think there's pretty much no chance of accepting more than one page
per

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#50José Luis Tallón
jltallon@adv-solutions.net
In reply to: Andres Freund (#49)
Re: Sequence Access Method WIP

On 12/03/2014 11:24 AM, Andres Freund wrote:

On 2014-12-03 10:59:50 +0100, Jos� Luis Tall�n wrote:

snip]

I don't think the WAL logging would need to change much in comparison to
the current solution. We'd just add the page number to the WAL record.

The biggest advantage would be to require fewer heavyweight locks,
because the pg_sequence one could be a single fastpath lock. Currently
we have to take the sequence's relation lock (heavyweight) and then the
the page level lock (lwlock) for every single sequence used.

Got it, thank you for the explanation.

May I possibly suggest a file-per-schema model instead? This approach would
certainly solve the excessive i-node consumption problem that --I guess--
Andres is trying to address here.

I don't think that really has any advantages.

Just spreading the I/O load, nothing more, it seems:

Just to elaborate a bit on the reasoning, for completeness' sake:
Given that a relation's segment maximum size is 1GB, we'd have
(1048576/8)=128k sequences per relation segment.
Arguably, not many real use cases will have that many sequences.... save
for *massively* multi-tenant databases.

The downside being that all that random I/O --- in general, it can't
really be sequential unless there are very very few sequences--- can't
be spread to other spindles. Create a "sequence_default_tablespace" GUC
+ ALTER SEQUENCE SET TABLESPACE, to use an SSD for this purpose maybe?
(I could take a shot at the patch, if deemed worthwhile)

[snip]

The overhead of a file is much more than wasting the remainder of a
page. Alone the requirement of separate fsyncs and everything is pretty
bothersome. The generated IO patterns are also much worse...

Yes, you are right. I stand corrected.

[snip]
I think there's pretty much no chance of accepting more than one page
per sequence

Definitively.

Thanks,

J.L.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#51Jim Nasby
Jim.Nasby@BlueTreble.com
In reply to: José Luis Tallón (#50)
Re: Sequence Access Method WIP

On 12/3/14, 8:50 AM, Jos� Luis Tall�n wrote:

May I possibly suggest a file-per-schema model instead? This approach would
certainly solve the excessive i-node consumption problem that --I guess--
Andres is trying to address here.

I don't think that really has any advantages.

Just spreading the I/O load, nothing more, it seems:

Just to elaborate a bit on the reasoning, for completeness' sake:
Given that a relation's segment maximum size is 1GB, we'd have (1048576/8)=128k sequences per relation segment.
Arguably, not many real use cases will have that many sequences.... save for *massively* multi-tenant databases.

The downside being that all that random I/O --- in general, it can't really be sequential unless there are very very few sequences--- can't be spread to other spindles. Create a "sequence_default_tablespace" GUC + ALTER SEQUENCE SET TABLESPACE, to use an SSD for this purpose maybe?

Why not? RAID arrays typically use stripe sizes in the 128-256k range, which means only 16 or 32 sequences per stripe.

It still might make sense to allow controlling what tablespace a sequence is in, but IMHO the default should just be pg_default.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#52Andres Freund
andres@2ndquadrant.com
In reply to: José Luis Tallón (#50)
Re: Sequence Access Method WIP

May I possibly suggest a file-per-schema model instead? This approach would
certainly solve the excessive i-node consumption problem that --I guess--
Andres is trying to address here.

I don't think that really has any advantages.

Just spreading the I/O load, nothing more, it seems:

Just to elaborate a bit on the reasoning, for completeness' sake:
Given that a relation's segment maximum size is 1GB, we'd have
(1048576/8)=128k sequences per relation segment.
Arguably, not many real use cases will have that many sequences.... save for
*massively* multi-tenant databases.

The downside being that all that random I/O --- in general, it can't really
be sequential unless there are very very few sequences--- can't be spread to
other spindles. Create a "sequence_default_tablespace" GUC + ALTER SEQUENCE
SET TABLESPACE, to use an SSD for this purpose maybe?
(I could take a shot at the patch, if deemed worthwhile)

I think that's just so far outside the sane usecases that I really don't
see us adding complexity to reign it in. If your frequently used
sequences get flushed out to disk by anything but the checkpointer the
schema is just badly designed...

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#53Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#45)
2 attachment(s)
Re: Sequence Access Method WIP

On 24/11/14 12:16, Heikki Linnakangas wrote:

I'd like to see two changes to this proposal:

1. Make the AM implementation solely responsible for remembering the
"last value". (if it's a global or remote sequence, the current value
might not be stored in the local server at all)

2. Instead of the single amdata field, make it possible for the
implementation to define any number of fields with any datatype in the
tuple. That would make debugging, monitoring etc. easier.

Hi,

here is the new version for next CF, there are several rough edges (more
about that later) but I think it's ready for more feedback.

I did change the API quite considerably (rewrite is the word I believe).

Instead of trying to put smarts on one or the other side of the API, I
basically created 2 APIs, one is the seqam which is responsible for
maintaining the sequence data and the other is sequence storage API
provided by postgres backend.

This means that backend cares purely about storage, and local caching
while AM cares about the data inside sequence, and decides when to WAL.

I'll start with storage API description:
- sequence_open(Oid) - open sequence with given Oid and returns
SequenceHandle (opens relation in share lock only)

- sequence_read_tuple(SequenceHandle) - returns heap tuple of the
sequence, this will also do the buffer locking

- sequence_save_tuple(SequenceHandle, newtuple, do_wal) - updates the
buffer with new sequence, optionally does WAL logging, newtuple can be
NULL in which case the tuple is not replaced (this is performance
optimization for sequences that don't need varlena/nullable columns and
can update everything inplace)

- sequence_release_tuple(SequenceHandle) - unlocks previously locked
buffer - usable for synchronization inside the seqam implementation, the
gapless sequence uses this

- sequence_close(SequenceHandle) - closes the sequence handle, also
unlocks buffer if needed

- sequence_needs_wal(SequenceHandle) - returns true if sequence needs
wal, that is if the last wal write happened before last checkpoint -
this is a little bit low level but I don't see a way around it if the
local sequence is supposed to maintain last_value and logcnt itself,
also we have similar function for relations (that checks for different
things but naming and usage is similar) so it's probably not too bad

The SequenceHandle is opaque struct so no gory details seen by the AM.

Now for the actual sequence AM API:
- seqam_extra_columns(Oid) - returns list of custom columns needed by
specified sequenceAM

- seqam_init - Initializes the columns for the custom sequenceAM, used
when creating, altering or resetting sequence. It gets sequenceam Oid,
sequence options, reloptions on input and returns updated values and
nulls arrays.

- seqam_alloc - Allocates new sequence values. It gets sequence
relation, handle and how many new values the backend wants and returns
first and last allocated value.

- seqam_setval - implements setval() support

- seqam_dump_state - pg_dump support
- seqam_restore_state - pg_dump support

The API is slightly bigger, but it also includes proper support for
dumping and restoring via pg_dump and seems somewhat cleaner, plus the
synchronous and asynchronous APIs are now same and no callbacks anywhere
(if AM needs to do something asynchronously it just calls the storage API).

There is also gapless sequence contrib module included as separate
patch, it's missing proper locking during dump/restore atm but otherwise
should be fully working (see tests). I am also using it for testing the
ALTER TABLE USING.

About the rough edges:
- The AlterSequence is not prettiest code around as we now have to
create new relation when sequence AM is changed and I don't know how to
do that nicely
- I am not sure if I did the locking, command order and dependency
handling in AlterSequence correcly
- I removed the setval3 since there is no guarantee that sequence will
have is_called parameter but there is now proper pg_dump support so it's
not needed for pg_dump usecase, but it's still incompatible change
- ResetSequence basically does ALTER SEQUENCE RESET internally instead
calling setval now because of the above
- there are no docs for the C APIs yet - I don't even know where those
should be placed btw, would appreciate suggestions (I have same problem
with committs API docs)

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-seqam-v4.patchtext/x-diff; name=0001-seqam-v4.patchDownload
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..818da15 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+			  transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index c16b38e..35818c0 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -321,6 +321,7 @@ static bool need_initialization = true;
 static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 					int text_len, bool validate);
+static bytea *common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate);
 
 /*
  * initialize_reloptions
@@ -821,7 +822,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -854,6 +856,9 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 		case RELKIND_INDEX:
 			options = index_reloptions(amoptions, datum, false);
 			break;
+		case RELKIND_SEQUENCE:
+			options = sequence_reloptions(amoptions, datum, false);
+			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
 			break;
@@ -1299,13 +1304,31 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 /*
  * Parse options for indexes.
+ */
+bytea *
+index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for sequences.
+ */
+bytea *
+sequence_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
-bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+static bytea *
+common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..ce57f16
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,428 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAm. That is also the basic default.  Local
+ * SeqAm assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAm mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAm would also wish to control node-level caches for sequences to
+ * ensure good performance.  The CACHE option and other options may be
+ * overridden by the _alloc API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAm allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module.  The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, am_data.
+ *
+ * We currently assume that there is no persistent state held within the SeqAm,
+ * so it is safe to ALTER the access method of an object without taking any
+ * special actions, as long as we hold an AccessExclusiveLock while we do that.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAm data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*default_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_aminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Manager API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_extra_columns - get list of extra columns needed by the am
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_seqparams		- process the standard sequence parameters
+ *		seqam_dump_state	- dump sequence state (for pg_dump)
+ *		seqam_restore_state - restore sequence state (for pg_dump)
+ *
+ *		sequence_reloptions	- process reloptions - located in reloptions.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_extra_columns - get custom column list
+ */
+List *
+seqam_extra_columns(Oid seqamid)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple = NULL;
+	Form_pg_seqam seqamForm;
+	FunctionCallInfoData fcinfo;
+	Datum		ret;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	if (!RegProcedureIsValid(seqamForm->seqamextracols))
+		return NIL;
+
+	fmgr_info(seqamForm->seqamextracols, &procedure);
+
+	/*
+	 * have the seqam's proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 0, InvalidOid, NULL, NULL);
+
+	ret = FunctionCallInvoke(&fcinfo);
+
+	ReleaseSysCache(tuple);
+
+	return (List *)DatumGetPointer(ret);
+}
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+extern void
+seqam_init(Oid seqamid, List *seqparams,
+		   List *reloptions, bool is_init,
+		   Datum *values, bool *nulls)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple = NULL;
+	Form_pg_seqam seqamForm;
+	FunctionCallInfoData fcinfo;
+	char	   *validnsps[] = {NULL, NULL};
+	Datum       reloptions_transformed;
+	bytea	   *reloptions_parsed;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	fmgr_info(seqamForm->seqaminit, &procedure);
+
+	/* Allow am specific options */
+	validnsps[0] = NameStr(seqamForm->seqamname);
+
+	/*
+	 *  Parse AM-specific options, convert to text array form,
+	 *  retrieve the AM-option function and then validate.
+	 */
+	reloptions_transformed = transformRelOptions((Datum) NULL, reloptions,
+												 NULL, validnsps, false,
+												 false);
+
+	reloptions_parsed = sequence_reloptions(seqamForm->seqamreloptions,
+											reloptions_transformed, true);
+
+	/*
+	 * Have the seqam's proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqparams);
+	fcinfo.arg[1] = PointerGetDatum(reloptions_parsed);
+	fcinfo.arg[2] = BoolGetDatum(is_init);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.arg[4] = PointerGetDatum(nulls);
+	fcinfo.argnull[0] = seqparams == NULL;
+	fcinfo.argnull[1] = reloptions_parsed == NULL;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[3] = false;
+
+	FunctionCallInvoke(&fcinfo);
+
+	ReleaseSysCache(tuple);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(nrequested);
+	fcinfo.arg[3] = PointerGetDatum(last);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 3, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(new_value);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * seqam_dump_state - pg_dump support
+ */
+char *
+seqam_dump_state(Relation seqrel, SequenceHandle *seqh)
+{
+	FmgrInfo	procedure;
+	Datum		ret;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamdump, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 2, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+
+	return DatumGetCString(ret);
+}
+
+/*
+ * seqam_restore_state - restore from pg_dump
+ */
+void
+seqam_restore_state(Relation seqrel, SequenceHandle *seqh, char *state)
+{
+	FmgrInfo	procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamrestore, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = CStringGetDatum(state);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new default_sequenceam */
+bool
+check_default_seqam(char **newval, void **extra, GucSource source)
+{
+	if (**newval == '\0')
+		return true;
+
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.	Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence manager \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence manager \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+/*
+ * GetDefaultSeqAM -- get the OID of the current default sequence AM
+ *
+ * This exists to hide (and possibly optimize the use of) the
+ * default_seqam GUC variable.
+ */
+Oid
+GetDefaultSeqAM(void)
+{
+	/* Fast path for default_tablespace == "" */
+	if (default_seqam == NULL || default_seqam[0] == '\0')
+		return LOCAL_SEQAM_OID;
+
+	return get_seqam_oid(default_seqam, false);
+}
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("squence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..9cc5f11
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,405 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct FormLocalSequence
+{
+	FormData_pg_sequence seq;
+	int64		last_value;
+	int64		log_cnt;
+	bool		is_called;
+} FormLocalSequence;
+
+SeqAMColumnData seqam_local_cols[] = {
+	{"last_value", INT8OID, -1, true},
+	{"log_cnt", INT8OID, -1, true},
+	{"is_called", BOOLOID, -1, true}
+};
+#define SEQAM_LOCAL_EXTRACOLS_CNT 3
+#define SEQAM_LOCAL_COL_LASTVAL SEQ_COL_LASTCOL + 1
+#define SEQAM_LOCAL_COL_LOGCNT SEQ_COL_LASTCOL + 2
+#define SEQAM_LOCAL_COL_CALLED SEQ_COL_LASTCOL + 3
+
+/*
+ * seqam_local_extracols()
+ *
+ * Get definitions for extra columns needed by a local sequence
+ */
+Datum
+seqam_local_extracols(PG_FUNCTION_ARGS)
+{
+	List *ret = NIL;
+	int i;
+
+	for (i = 0; i < SEQAM_LOCAL_EXTRACOLS_CNT; i++)
+		ret = lappend(ret, &seqam_local_cols[i]);
+
+	PG_RETURN_POINTER(ret);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum       reloptions = PG_GETARG_DATUM(0);
+	bool        validate = PG_GETARG_BOOL(1);
+	bytea      *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	List *params = (List *)(PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));
+	bool is_init = PG_GETARG_BOOL(2);
+	Datum *values = (Datum *)PG_GETARG_POINTER(3);
+	bool *nulls = (bool *)PG_GETARG_POINTER(4);
+
+	DefElem    *restart_value = NULL;
+	int64		last_value,
+				min_value,
+				max_value;
+	ListCell   *param;
+
+	foreach(param, params)
+	{
+		DefElem    *defel = (DefElem *) lfirst(param);
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			/* The redundacy check is done in the main sequence code. */
+			restart_value = defel;
+			break;
+		}
+	}
+
+	/* RESTART [WITH] */
+	if (restart_value != NULL)
+	{
+		Datum		last_value;
+
+		if (restart_value->arg != NULL)
+			last_value = defGetInt64(restart_value);
+		else
+			last_value = values[SEQ_COL_STARTVAL - 1];
+
+		values[SEQAM_LOCAL_COL_LASTVAL - 1] = last_value;
+		values[SEQAM_LOCAL_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+	else if (is_init)
+	{
+		values[SEQAM_LOCAL_COL_LASTVAL - 1] = values[SEQ_COL_STARTVAL - 1];
+		values[SEQAM_LOCAL_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+
+	last_value = DatumGetInt64(values[SEQAM_LOCAL_COL_LASTVAL - 1]);
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+
+	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
+	if (last_value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, last_value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
+					  bufs, bufm)));
+	}
+	if (last_value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, last_value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
+				   bufs, bufm)));
+	}
+
+	/* log_cnt */
+	values[SEQAM_LOCAL_COL_LOGCNT - 1] = Int64GetDatum(0);
+
+	nulls[SEQAM_LOCAL_COL_LASTVAL - 1] = false;
+	nulls[SEQAM_LOCAL_COL_LOGCNT - 1] = false;
+	nulls[SEQAM_LOCAL_COL_CALLED - 1] = false;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormLocalSequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				logit = false;
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	next = result = seq->last_value;
+	incby = seq->seq.increment_by;
+	maxv = seq->seq.max_value;
+	minv = seq->seq.min_value;
+	is_cycled = seq->seq.is_cycled;
+	fetch = nrequested;
+	log = seq->log_cnt;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!seq->is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !seq->is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (seq->is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	START_CRIT_SECTION();
+
+	/* Log our cached data. */
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		seq->log_cnt = 0;
+
+		sequence_save_tuple(seqh, NULL, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	seq->log_cnt = log;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, false);
+
+	END_CRIT_SECTION();
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    next = PG_GETARG_INT64(2);
+	FormLocalSequence *seq;
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	seq->log_cnt = 0;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_dump()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ *
+ * Format is: '<last_value::bigint>, <is_called::boolean>'
+ */
+Datum
+seqam_local_dump(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	FormLocalSequence  *seq;
+	char		buf[19 + 1 + 1 + 1 + 1]; /* int64 + colon + space + t/f + null byte */
+	char	   *result;
+
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	pg_lltoa(seq->last_value, buf);
+	strcat(buf, seq->is_called ? ", t" : ", f");
+	result = pstrdup(buf);
+
+	PG_RETURN_CSTRING(result);
+}
+
+/*
+ * seqam_local_restore()
+ *
+ * Restore previously dumpred state of local sequence (used by pg_dump)
+ *
+ * Format is: '<last_value::bigint>, <is_called::boolean>'
+*/
+Datum
+seqam_local_restore(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			   *state = PG_GETARG_CSTRING(2);
+	FormLocalSequence  *seq;
+	int64				last_value;
+	bool				is_called;
+	char			   *buf;
+	char			   *ptr = state;
+	int					len;
+
+	/* Check that we can find comma. */
+	while (*ptr != ',' && *ptr != '\0')
+		ptr++;
+
+	if (*ptr == '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for local sequence state: \"%s\"",
+						state)));
+
+	/*
+	 * Copy the part of the string before comma into work buffer
+	 * for scanint8, it will handle leading/trailing whitespace correctly.
+	 */
+	len = ptr - state;
+	buf = palloc(len + 1);
+	memcpy(buf, state, len);
+	buf[len] = '\0';
+	scanint8(buf, false, &last_value);
+
+	/* Skip leading/trailing whitespace for parse_bool_with_len. */
+	ptr++;
+	while (isspace((unsigned char) *ptr))
+		ptr++;
+
+	len = strlen(ptr);
+	while (len > 0 && isspace((unsigned char) ptr[len - 1]))
+		len--;
+
+	/* Parse out the boolean */
+	if (!parse_bool_with_len(ptr, len, &is_called))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for local sequence state: \"%s\"",
+						state)));
+
+	pfree(buf);
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->last_value = last_value;
+	seq->is_called = is_called;
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a403c64..147c571 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index ba5b938..e062081 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,15 +14,21 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -35,19 +41,13 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -80,6 +80,14 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTuple	tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -90,13 +98,124 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static Oid get_new_seqam_oid(Oid oldAM, char *accessMethod);
+
+
+/*
+ * Build template column definition for a sequence relation.
+ */
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_from_type = false;
+	coldef->storage = 0;
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Create the relation for sequence.
+ */
+static Oid
+DefineSequenceRelation(RangeVar *rel, List *cols,
+					   bool if_not_exists, Oid ownerId)
+{
+	CreateStmt *stmt = makeNode(CreateStmt);
+
+	stmt->relation = rel;
+	stmt->tableElts = cols;
+	stmt->inhRelations = NIL;
+	stmt->constraints = NIL;
+	stmt->options = NIL;
+	stmt->oncommit = ONCOMMIT_NOOP;
+	stmt->tablespacename = NULL;
+	stmt->if_not_exists = if_not_exists;
+
+	return DefineRelation(stmt, RELKIND_SEQUENCE, ownerId);
+}
+
+/*
+ * Add additional sequence am columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(List *amcols)
+{
+	List	   *seqcols;
+	ListCell   *amcol;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		coldef->is_not_null = true;
+
+		switch (colid)
+		{
+			case SEQ_COL_NAME:
+				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
+				coldef->colname = "sequence_name";
+				break;
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+		}
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	foreach(amcol, amcols)
+	{
+		SeqAMColumn	amcolumn = lfirst(amcol);
+
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		coldef->typeName = makeTypeNameFromOid(amcolumn->coltyp,
+											   amcolumn->coltypmod);
+		coldef->colname = amcolumn->colname;
+		coldef->is_not_null = amcolumn->colnotnull;
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	return seqcols;
+}
 
 
 /*
@@ -108,15 +227,17 @@ DefineSequence(CreateSeqStmt *seq)
 {
 	FormData_pg_sequence new;
 	List	   *owned_by;
-	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	Relation	rel;
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
+	Datum	   *value;
+	bool	   *null;
+	int			colid;
 	NameData	name;
+	List	   *seqcols,
+			   *amcols;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -142,104 +263,75 @@ DefineSequence(CreateSeqStmt *seq)
 		}
 	}
 
-	/* Check and set all option values */
+	/* Check and set all param values */
 	init_params(seq->options, true, &new, &owned_by);
 
+	/* Build column definitions. */
+	seqamid = get_new_seqam_oid(InvalidOid, seq->accessMethod);
+	amcols = seqam_extra_columns(seqamid);
+
+	seqcols = BuildSeqColumnDefList(amcols);
+
+	value = palloc0(list_length(seqcols) * sizeof(Datum));
+	null = palloc0(list_length(seqcols) * sizeof(bool));
+
 	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
+	 * Fill value[] and null[] for the tuple.
 	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
 	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
+		null[colid - 1] = false;
 
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
+		switch (colid)
 		{
 			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
 				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
+				value[colid - 1] = NameGetDatum(&name);
 				break;
 			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
+				value[colid - 1] = Int64GetDatumFast(new.start_value);
 				break;
 			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
+				value[colid - 1] = Int64GetDatumFast(new.increment_by);
 				break;
 			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
+				value[colid - 1] = Int64GetDatumFast(new.max_value);
 				break;
 			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
+				value[colid - 1] = Int64GetDatumFast(new.min_value);
 				break;
 			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
+				value[colid - 1] = Int64GetDatumFast(new.cache_value);
 				break;
 			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
+				value[colid - 1] = BoolGetDatum(new.is_cycled);
 				break;
 		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
 
-	stmt->relation = seq->sequence;
-	stmt->inhRelations = NIL;
-	stmt->constraints = NIL;
-	stmt->options = NIL;
-	stmt->oncommit = ONCOMMIT_NOOP;
-	stmt->tablespacename = NULL;
-	stmt->if_not_exists = seq->if_not_exists;
+	/* Let AM fill the value[] and null[] for the tuple as well. */
+	seqam_init(seqamid, seq->options, seq->amoptions, true,
+			   value, null);
 
-	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
+	/* Create the sequence relation */
+	seqoid = DefineSequenceRelation(seq->sequence, seqcols,
+									seq->if_not_exists, seq->ownerId);
 	Assert(seqoid != InvalidOid);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
 	/* now initialize the sequence's data */
+	tupDesc = RelationGetDescr(rel);
 	tuple = heap_form_tuple(tupDesc, value, null);
+
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -266,56 +358,68 @@ DefineSequence(CreateSeqStmt *seq)
 void
 ResetSequence(Oid seq_relid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	HeapTuple	newtup;
+	Relation	seqrel;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle  *seqh;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	seqh = sequence_open(seq_relid);
+	tuple = sequence_read_tuple(seqh);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
-	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	seqrel = seqh->rel;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	memset(replaces, true, tupDesc->natts * sizeof(bool));
+
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+
+	seqam_init(seqrel->rd_rel->relam,
+			   list_make1(makeDefElem("restart", NULL)), NULL, false,
+			   values, nulls);
+
+	newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqh->rel, seqh->rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqh->rel, newtup);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh->elm->cached = seqh->elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(seqh);
 }
 
 /*
@@ -368,23 +472,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -400,16 +488,23 @@ Oid
 AlterSequence(AlterSeqStmt *stmt)
 {
 	Oid			relid;
+	Oid			oldamid;
+	Oid			seqamid;
 	SeqTable	elm;
+	HeapTuple	tuple;
+	HeapTuple	newtup;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle *seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
+	relid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
 	if (relid == InvalidOid)
 	{
 		ereport(NOTICE,
@@ -418,7 +513,9 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidOid;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
+	elm = seqh->elm;
+	seqrel = seqh->rel;
 
 	/* allow ALTER to sequence owner only */
 	if (!pg_class_ownercheck(relid, GetUserId()))
@@ -426,48 +523,90 @@ AlterSequence(AlterSeqStmt *stmt)
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
-
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
-
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	init_params(stmt->options, false, new, &owned_by);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+	oldamid = seqrel->rd_rel->relam;
+	seqamid = get_new_seqam_oid(seqrel->rd_rel->relam, stmt->accessMethod);
 
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
+	/*
+	 * If we are changing sequence AM, we need to replace
+	 * the sequence relation.
+	 */
+	if (seqamid != oldamid)
 	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		List   *amcols = seqam_extra_columns(seqamid);
+		List   *seqcols;
+		int		natts;
+		Oid		ownerId = seqrel->rd_rel->relowner;
+		ObjectAddress object;
+
+		/* Remove the old sequence relation. */
+		sequence_close(seqh);
+		markSequenceUnowned(relid);
+		object.classId = RelationRelationId;
+		object.objectId = relid;
+		object.objectSubId = 0;
+		performDeletion(&object, DROP_RESTRICT, 0);
+
+		/* Create the sequence relation */
+		seqcols = BuildSeqColumnDefList(amcols);
+		relid = DefineSequenceRelation(stmt->sequence, seqcols, false, ownerId);
+		Assert(relid != InvalidOid);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		/*
+		 * After we've created the sequence's relation in pg_class, update
+		 * the relam to a non-default value, if requested. We perform this
+		 * as a separate update to avoid invasive changes in normal code
+		 * paths and to keep the code similar between CREATE and ALTER.
+		 */
+		seqrel_update_relam(relid, seqamid);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		/* Open new sequence. */
+		seqh = sequence_open(relid);
+		elm = seqh->elm;
+		seqrel = seqh->rel;
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		/*
+		 * We need to fit both old columns and new columns to the
+		 * values/nulls/replaces arrays.
+		 */
+		tupDesc = RelationGetDescr(seqrel);
+		natts = Max(tupDesc->natts, list_length(seqcols));
+		values = (Datum *) palloc(natts * sizeof(Datum));
+		nulls = (bool *) palloc(natts * sizeof(bool));
+	}
+	else
+	{
+		tupDesc = RelationGetDescr(seqrel);
+		values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+		nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	}
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+	replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	memset(replaces, true, tupDesc->natts * sizeof(bool));
 
-		PageSetLSN(page, recptr);
-	}
+	/* Let sequence AM update the tuple. */
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	seqam_init(seqamid, stmt->options, stmt->amoptions, seqamid != oldamid,
+			   values, nulls);
+	newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
 
-	END_CRIT_SECTION();
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	elm->cached = elm->last;
 
-	UnlockReleaseBuffer(buf);
+	/* Now okay to update the on-disk tuple */
+	if (seqamid != oldamid)
+		fill_seq_with_data(seqh->rel, newtup);
+	else
+		sequence_save_tuple(seqh, newtup, true);
 
 	/* process OWNED BY if given */
 	if (owned_by)
@@ -475,7 +614,7 @@ AlterSequence(AlterSeqStmt *stmt)
 
 	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(seqh);
 
 	return relid;
 }
@@ -516,29 +655,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle *seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
+	elm = seqh->elm;
+	seqrel = seqh->rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -556,121 +690,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
 
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -679,261 +707,119 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_close(seqh);
 
 	return result;
 }
 
+
 Datum
 currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle *seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh->elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
-
-	if (!elm->last_valid)
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
-
-	result = elm->last;
-
-	relation_close(seqrel, NoLock);
-
-	PG_RETURN_INT64(result);
-}
-
-Datum
-lastval(PG_FUNCTION_ARGS)
-{
-	Relation	seqrel;
-	int64		result;
-
-	if (last_used_seq == NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("lastval is not yet defined in this session")));
-
-	/* Someone may have dropped the sequence since the last nextval() */
-	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("lastval is not yet defined in this session")));
-
-	seqrel = open_share_lock(last_used_seq);
-
-	/* nextval() must have already been called for this sequence */
-	Assert(last_used_seq->last_valid);
-
-	if (pg_class_aclcheck(last_used_seq->relid, GetUserId(),
-						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
-
-	result = last_used_seq->last;
-	relation_close(seqrel, NoLock);
-
-	PG_RETURN_INT64(result);
-}
-
-/*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
- */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
-{
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-
-	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
-
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh->rel))));
 
-	/* read-only transactions may only modify temp sequences */
-	if (!seqrel->rd_islocaltemp)
-		PreventCommandIfReadOnly("setval()");
-
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
+	if (!seqh->elm->last_valid)
 		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
-
-	/* In any case, forget any future cached numbers */
-	elm->cached = elm->last;
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
+						RelationGetRelationName(seqh->rel))));
 
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
+	result = seqh->elm->last;
 
-	MarkBufferDirty(buf);
+	sequence_close(seqh);
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+	PG_RETURN_INT64(result);
+}
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+Datum
+lastval(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel;
+	int64		result;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+	if (last_used_seq == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("lastval is not yet defined in this session")));
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+	/* Someone may have dropped the sequence since the last nextval() */
+	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("lastval is not yet defined in this session")));
 
-		PageSetLSN(page, recptr);
-	}
+	seqrel = open_share_lock(last_used_seq);
 
-	END_CRIT_SECTION();
+	/* nextval() must have already been called for this sequence */
+	Assert(last_used_seq->last_valid);
 
-	UnlockReleaseBuffer(buf);
+	if (pg_class_aclcheck(last_used_seq->relid, GetUserId(),
+						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+						RelationGetRelationName(seqrel))));
 
+	result = last_used_seq->last;
 	relation_close(seqrel, NoLock);
+
+	PG_RETURN_INT64(result);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the setval procedure.
  */
 Datum
 setval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle *seqh;
 
-	do_setval(relid, next, true);
+	/* open and AccessShareLock sequence */
+	seqh = sequence_open(relid);
+	elm = seqh->elm;
+	seqrel = seqh->rel;
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+						RelationGetRelationName(seqrel))));
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-	do_setval(relid, next, iscalled);
+	seqam_setval(seqrel, seqh, next);
+
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
+
+	sequence_close(seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -993,21 +879,21 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+SequenceHandle *
+sequence_open(Oid seq_relid)
 {
-	SeqTable	elm;
-	Relation	seqrel;
-	bool		found;
+	SequenceHandle *seqh;
+	SeqTable		elm;
+	Relation		seqrel;
+	bool			found;
 
 	/* Find or create a hash table entry for this sequence */
 	if (seqhashtab == NULL)
 		create_seq_hashtable();
 
-	elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);
+	elm = (SeqTable) hash_search(seqhashtab, &seq_relid, HASH_ENTER, &found);
 
 	/*
 	 * Initialize the new hash table entry if it did not exist already.
@@ -1048,45 +934,60 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 		elm->cached = elm->last;
 	}
 
+	seqh = (SequenceHandle *) palloc(sizeof(SequenceHandle));
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup = NULL;
+
+	return seqh;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	sequence_release_tuple(seqh);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
+	Form_pg_sequence seq_form;
+
+	if (HeapTupleIsValid(seqh->tup))
+		return seqh->tup;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	seqh->tup = palloc0(HEAPTUPLESIZE);
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of seqh->tup */
+	seqh->tup->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup->t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1096,33 +997,116 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup->t_data) != InvalidTransactionId)
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		HeapTupleHeaderSetXmax(seqh->tup->t_data, InvalidTransactionId);
+		seqh->tup->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
 	}
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	seq_form = (Form_pg_sequence) GETSTRUCT(seqh->tup);
 
 	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	seqh->elm->increment = seq_form->increment_by;
+
+	return seqh->tup;
+}
+
+/*
+ * Update the page, optionally do WAL logging of the tuple
+ */
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal)
+{
+	Page	page;
+
+	Assert(HeapTupleIsValid(seqh->tup));
+
+	page = BufferGetPage(seqh->buf);
+
+	/* Only do tuple replacement if we got valid tuple on input. */
+	if (HeapTupleIsValid(newtup))
+	{
+		Page	temppage;
+
+		temppage = PageGetTempPageCopySpecial(page);
+
+		if (PageAddItem(temppage, (Item) newtup->t_data, newtup->t_len,
+						FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+			elog(PANIC, "sequence_save_tuple: failed to add item to page");
+
+		PageSetLSN(temppage, PageGetLSN(page));
+
+		START_CRIT_SECTION();
 
-	return seq;
+		PageRestoreTempPage(temppage, page);
+		seqh->tup = newtup;
+		MarkBufferDirty(seqh->buf);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, newtup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+	else
+	{
+		START_CRIT_SECTION();
+
+		MarkBufferDirty(seqh->buf);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, seqh->tup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+}
+
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (HeapTupleIsValid(seqh->tup))
+	{
+		pfree(seqh->tup);
+		seqh->tup = NULL;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
+	{
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
+	}
+}
+
+/*
+ * Returns true if sequence was not WAL logged since checkpoint
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr = GetRedoRecPtr();
+
+	Assert(BufferIsValid(seqh->buf));
+
+	page = BufferGetPage(seqh->buf);
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1132,13 +1116,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1209,13 +1193,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1224,7 +1201,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1234,7 +1210,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1243,7 +1218,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1251,14 +1225,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1266,7 +1238,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1320,48 +1291,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1376,14 +1305,13 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1399,8 +1327,7 @@ process_owned_by(Relation seqrel, List *owned_by)
 
 	nnames = list_length(owned_by);
 	Assert(nnames > 0);
-	if (nnames == 1)
-	{
+	if (nnames == 1)	{
 		/* Must be OWNED BY NONE */
 		if (strcmp(strVal(linitial(owned_by)), "none") != 0)
 			ereport(ERROR,
@@ -1487,20 +1414,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  *seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh->rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1518,7 +1442,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1526,12 +1450,99 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_close(seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_dump_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *result;
+	SequenceHandle  *seqh;
+
+	seqh = sequence_open(relid);
+
+	result = seqam_dump_state(seqh->rel, seqh);
+
+	sequence_close(seqh);
+
+	PG_RETURN_CSTRING(result);
+}
+
+Datum
+pg_sequence_restore_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *state = PG_GETARG_CSTRING(1);
+	SequenceHandle  *seqh;
+
+	seqh = sequence_open(relid);
+
+	seqam_restore_state(seqh->rel, seqh, state);
+
+	sequence_close(seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+		CatalogUpdateIndexes(rd, ctup);
+	}
+
+	heap_freetuple(ctup);
+	heap_close(rd, RowExclusiveLock);
+	CommandCounterIncrement();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1583,6 +1594,7 @@ seq_redo(XLogReaderState *record)
 	pfree(localpage);
 }
 
+
 /*
  * Flush cached sequence information.
  */
@@ -1597,3 +1609,92 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+static Oid
+get_new_seqam_oid(Oid oldAM, char *accessMethod)
+{
+
+	if (oldAM && accessMethod == NULL)
+		return oldAM;
+	else if (accessMethod == NULL || strcmp(accessMethod, DEFAULT_SEQAM) == 0)
+		return GetDefaultSeqAM();
+	else
+		return get_seqam_oid(accessMethod, false);
+}
+
+
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64 next = *value;
+	int64 rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1e737a0..0c50e6f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9046,6 +9046,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 		case RELKIND_INDEX:
 			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
+		case RELKIND_SEQUENCE:
+			(void) sequence_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6b1bf7b..c9f7f8e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3361,7 +3361,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3374,7 +3376,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d5db71d..90e765c 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1567,7 +1567,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1578,7 +1580,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b5009b..56ff30b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3503,7 +3504,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = DEFAULT_SEQAM;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3525,7 +3552,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3533,11 +3584,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3596,7 +3670,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 7c1939f..5994d18 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -399,6 +399,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index eca3f97..0aa4cf2 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 79244e5..d003a28 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1055,11 +1057,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1535,6 +1540,39 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for an index relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	MemoryContext	indexcxt;
+	Form_pg_seqam	amform;
+
+	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+									 RelationGetRelationName(rel),
+									 ALLOCSET_SMALL_MINSIZE,
+									 ALLOCSET_SMALL_INITSIZE,
+									 ALLOCSET_SMALL_MAXSIZE);
+	rel->rd_indexcxt = indexcxt;
+
+	rel->rd_aminfo = (RelationAmInfo *)
+		MemoryContextAllocZero(rel->rd_indexcxt,
+							   sizeof(RelationAmInfo));
+
+	/*
+	 * Make a copy of the pg_am entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+}
 
 /*
  *		formrdesc
@@ -4792,6 +4830,22 @@ load_relcache_init_file(bool shared)
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
+		else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+		{
+			MemoryContext indexcxt;
+			Assert(!rel->rd_isnailed);
+			Assert(false);
+
+			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+											 RelationGetRelationName(rel),
+											 ALLOCSET_SMALL_MINSIZE,
+											 ALLOCSET_SMALL_INITSIZE,
+											 ALLOCSET_SMALL_MAXSIZE);
+			rel->rd_indexcxt = indexcxt;
+			/* set up zeroed fmgr-info vectors */
+			rel->rd_aminfo = (RelationAmInfo *)
+				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+		}
 		else
 		{
 			/* Count nailed rels to ensure we have 'em all */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 94d951c..66deaa6 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAmNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_amname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAmOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b1bff7f..51c8982 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2788,6 +2789,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"default_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence am for any new sequences."),
+			gettext_noop("An empty string selects the 'local' sequence am."),
+			GUC_IS_NAME | GUC_NOT_IN_SAMPLE
+		},
+		&default_seqam,
+		"",
+		check_default_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4175ddc..f4d10e0 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14567,7 +14567,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = "local";
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14637,15 +14638,41 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	}
 #endif
 
-	startv = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
+	startv = pg_strdup(PQgetvalue(res, 0, 1));
+	incby = pg_strdup(PQgetvalue(res, 0, 2));
 	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
+		maxv = pg_strdup(PQgetvalue(res, 0, 3));
 	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
+		minv = pg_strdup(PQgetvalue(res, 0, 4));
+	cache = pg_strdup(PQgetvalue(res, 0, 5));
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
+	PQclear(res);
+
+	res = ExecuteSqlQuery(fout, "SELECT EXISTS(SELECT 1 "
+								"FROM pg_catalog.pg_class c, "
+								"pg_catalog.pg_namespace n "
+								"WHERE n.oid = c.relnamespace "
+								"AND nspname = 'pg_catalog'"
+								"AND c.relname = 'pg_seqam' "
+								"AND c.relkind = 'r');",
+						  PGRES_TUPLES_OK);
+	if (strcmp(PQgetvalue(res, 0, 0), "t") == 0)
+	{
+		PQclear(res);
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		amname = pg_strdup(PQgetvalue(res, 0, 0));
+	}
+
+	PQclear(res);
+
 	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
@@ -14687,6 +14714,7 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14753,8 +14781,6 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
-	PQclear(res);
-
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(delqry);
 	destroyPQExpBuffer(labelq);
@@ -14769,17 +14795,30 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
+	char	   *state;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	res = ExecuteSqlQuery(fout, "SELECT EXISTS(SELECT 1 "
+								"FROM pg_catalog.pg_proc p, "
+								"pg_catalog.pg_namespace n "
+								"WHERE n.oid = p.pronamespace "
+								"AND nspname = 'pg_catalog'"
+								"AND p.proname = 'pg_sequence_dump_state');",
+						  PGRES_TUPLES_OK);
+
+	if (strcmp(PQgetvalue(res, 0, 0), "t") == 0)
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(last_value::text || ', ' || is_called::text) FROM %s",
+						  fmtId(tbinfo->dobj.name));
+	else
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_dump_state(%s))",
+						  fmtId(tbinfo->dobj.name));
 
+	PQclear(res);
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
 	if (PQntuples(res) != 1)
@@ -14791,14 +14830,12 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+	state = PQgetvalue(res, 0, 0);
 
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+	appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_restore_state(");
 	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+	appendPQExpBuffer(query, ", %s);\n", state);
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 5a9ceca..6dc428d 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1372,30 +1372,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1439,6 +1415,14 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S' && !verbose)
+		appendPQExpBufferStr(&buf, " AND attname IN ('sequence_name', "
+							 "'start_value', 'increment_by', 'max_value', "
+							 "'min_value', 'cache_value', 'is_cycled')");
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
 	res = PSQLexec(buf.data);
@@ -1446,6 +1430,39 @@ describeOneTableDetails(const char *schemaname,
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1757,6 +1774,29 @@ describeOneTableDetails(const char *schemaname,
 		/* Footer information about a sequence */
 		PGresult   *result = NULL;
 
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
+		/*
+		 * If we get no rows back, don't show anything (obviously). We should
+		 * never get more than one row back, but if we do, just ignore it and
+		 * don't print anything.
+		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
+		PQclear(result);
+
 		/* Get the column that owns this sequence */
 		printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
 						  "\n   pg_catalog.quote_ident(relname) || '.' ||"
@@ -1774,6 +1814,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1783,11 +1825,6 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
-		/*
-		 * If we get no rows back, don't show anything (obviously). We should
-		 * never get more than one row back, but if we do, just ignore it and
-		 * don't print anything.
-		 */
 		PQclear(result);
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a538830..bf4cafa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -46,8 +46,9 @@ typedef enum relopt_kind
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
 	RELOPT_KIND_BRIN = (1 << 10),
+	RELOPT_KIND_SEQUENCE = (1 << 11),
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -272,6 +273,8 @@ extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
 extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
 				 bool validate);
+extern bytea *sequence_reloptions(RegProcedure amoptions, Datum reloptions,
+				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..4fd435e
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef struct SeqAMColumnData
+{
+	char   *colname;
+	Oid		coltyp;
+	int32	coltypmod;
+	bool	colnotnull;
+} SeqAMColumnData;
+
+typedef SeqAMColumnData *SeqAMColumn;
+
+extern char *default_seqam;
+
+extern Oid GetDefaultSeqAM(void);
+
+extern List *seqam_extra_columns(Oid seqamid);
+extern void seqam_init(Oid seqamid, List *seqparams,
+					   List *reloptions, bool is_init,
+					   Datum *values, bool *nulls);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern char *seqam_dump_state(Relation seqrel, SequenceHandle *seqh);
+extern void seqam_restore_state(Relation seqrel, SequenceHandle *seqh, char *state);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern SequenceHandle *sequence_open(Oid seq_relid);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+
+extern Datum seqam_local_extracols(PG_FUNCTION_ARGS);
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_dump(PG_FUNCTION_ARGS);
+extern Datum seqam_local_restore(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index bde1a84..2daffe4 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAmNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAmOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 910cfc6..a96347f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1814,10 +1814,12 @@ DATA(insert OID = 1575 (  currval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 20
 DESCR("sequence current value");
 DATA(insert OID = 1576 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 20 "2205 20" _null_ _null_ _null_ _null_  setval_oid _null_ _null_ _null_ ));
 DESCR("set sequence value");
-DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ ));
-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_ ));
+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_ ));
+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_ ));
 DESCR("I/O");
@@ -5042,6 +5044,22 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6022 (  seqam_local_extracols	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ seqam_local_extracols _null_ _null_ _null_ ));
+DESCR("Get local SequenceAM extra columns");
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 2281 "2281 16" _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "2281 17 16 2281 2281" _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_dump	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 2281" _null_ _null_ _null_ _null_ seqam_local_dump _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_restore	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 17" _null_ _null_ _null_ _null_ seqam_local_restore _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 DESCR("list objects dropped by the current command");
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..e08c935
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,78 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	32
+
+CATALOG(pg_seqam,32)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamextracols;		/* get extra columns this am stores in the sequence relation */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamdump;			/* dump state, used by pg_dump */
+	regproc		seqamrestore;		/* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						8
+#define Anum_pg_seqam_amname				1
+#define Anum_pg_seqam_amextracols			2
+#define Anum_pg_seqam_amreloptions			3
+#define Anum_pg_seqam_aminit				4
+#define Anum_pg_seqam_amalloc				5
+#define Anum_pg_seqam_amsetval				6
+#define Anum_pg_seqam_amdump				7
+#define Anum_pg_seqam_amrestore				8
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 2 (  local		seqam_local_extracols seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_dump seqam_local_restore));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 2
+
+#define DEFAULT_SEQAM	""
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 386f1e6..8688620 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -23,15 +23,12 @@
 typedef struct FormData_pg_sequence
 {
 	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,18 +38,14 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  */
 
 #define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
-
+#define SEQ_COL_STARTVAL		2
+#define SEQ_COL_INCBY			3
+#define SEQ_COL_MAXVALUE		4
+#define SEQ_COL_MINVALUE		5
+#define SEQ_COL_CACHE			6
+#define SEQ_COL_CYCLE			7
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_CYCLE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -67,10 +60,11 @@ extern Datum nextval(PG_FUNCTION_ARGS);
 extern Datum nextval_oid(PG_FUNCTION_ARGS);
 extern Datum currval_oid(PG_FUNCTION_ARGS);
 extern Datum setval_oid(PG_FUNCTION_ARGS);
-extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_dump_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_restore_state(PG_FUNCTION_ARGS);
 
 extern Oid	DefineSequence(CreateSeqStmt *stmt);
 extern Oid	AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5eaa435..955b9c4 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2011,8 +2011,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2020,8 +2022,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 80813d2..507044e 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -404,6 +404,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_default_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 48ebf59..2998688 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -48,10 +49,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
+ * functions, defined by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+	/* pg_am only */
 	FmgrInfo	aminsert;
 	FmgrInfo	ambeginscan;
 	FmgrInfo	amgettuple;
@@ -61,6 +64,14 @@ typedef struct RelationAmInfo
 	FmgrInfo	ammarkpos;
 	FmgrInfo	amrestrpos;
 	FmgrInfo	amcanreturn;
+	FmgrInfo	amcostestimate;
+
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+
+	/* Common */
+	FmgrInfo	amoptions;
 } RelationAmInfo;
 
 
@@ -131,23 +142,25 @@ typedef struct RelationData
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+
 	/*
-	 * index access support info (used only for an index relation)
+	 * Access support info (used only for index or sequence relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
 	 * arrays are indexed by support function number, which is a sufficient
 	 * identifier given that restriction.
 	 *
-	 * Note: rd_amcache is available for index AMs to cache private data about
-	 * an index.  This must be just a cache since it may get reset at any time
+	 * Note: rd_amcache is available for AMs to cache private data about
+	 * an object.  This must be just a cache since it may get reset at any time
 	 * (in particular, it will get reset by a relcache inval message for the
 	 * index).  If used, it must point to a single memory chunk palloc'd in
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
-	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am */
+	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am or pg_seqam */
 	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
@@ -158,7 +171,7 @@ typedef struct RelationData
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
-	void	   *rd_amcache;		/* available for use by index AM */
+	void	   *rd_amcache;		/* available for use by AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
 
 	/*
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f97229f..2810a8d 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 7991e99..6b6493e 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -719,10 +719,10 @@ CREATE FUNCTION foo_mat(int,int) RETURNS setof foo_rescan_t AS 'begin for i in $
 --invokes ExecReScanFunctionScan - all these cases should materialize the function only once
 -- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function
 -- is on the inner path of a nestloop join
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) ON (r+i)<100;
@@ -739,10 +739,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) ON (r+i)<100;
  3 | 13 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
@@ -759,10 +759,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) WITH ORDINALITY
  3 | 13 | 3 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) ON (r+i)<100;
@@ -779,10 +779,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) ON (r+i)<100;
  3 | 13 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
@@ -799,10 +799,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) WITH ORDINALITY
  3 | 13 | 3 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( foo_sql(11,13), foo_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100;
@@ -876,10 +876,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) WITH O
 (9 rows)
 
 --invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL)
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13);
@@ -893,10 +893,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13) WITH ORDINALITY AS f(i,s,o);
@@ -910,10 +910,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 1
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r);
@@ -927,10 +927,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r) WITH ORDINALITY AS f(i,s,o);
@@ -944,10 +944,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 3
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2);
@@ -965,10 +965,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2);
  16 | 20 | 20 | 10
 (10 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2) WITH ORDINALITY AS f(i,s,o);
@@ -986,10 +986,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2) WITH ORD
  16 | 20 | 20 | 10 | 5
 (10 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13);
@@ -1003,10 +1003,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13) WITH ORDINALITY AS f(i,s,o);
@@ -1020,10 +1020,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 1
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r);
@@ -1037,10 +1037,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r) WITH ORDINALITY AS f(i,s,o);
@@ -1054,10 +1054,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 3
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2);
@@ -1075,10 +1075,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2);
  16 | 20 | 20 | 10
 (10 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2) WITH ORDINALITY AS f(i,s,o);
@@ -1097,10 +1097,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2) WITH ORD
 (10 rows)
 
 -- selective rescan of multiple functions:
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_mat(10+r,13) );
@@ -1114,10 +1114,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_mat(10+r
  3 | 11 | 1 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(11,11) );
@@ -1131,10 +1131,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(11
  3 | 13 | 6 | 11 | 1
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(10+r,13) );
@@ -1148,10 +1148,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(10
  3 | 13 | 6 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( foo_sql(10+r1,13), foo_mat(10+r2,13) );
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index c7be273..d4e250f 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..ea45a2d 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_restore_state('sequence_test'::text, '99, false');
+ pg_sequence_restore_state 
+---------------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_restore_state('sequence_test'::regclass, '99, false');
+ pg_sequence_restore_state 
+---------------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | last_value | log_cnt | is_called 
+---------------+-------------+--------------+---------------------+-----------+-------------+-----------+------------+---------+-----------
+ foo_seq       |           1 |            1 | 9223372036854775807 |         1 |           1 | f         |          1 |       0 | f
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | last_value | log_cnt | is_called 
+---------------+-------------+--------------+---------------------+-----------+-------------+-----------+------------+---------+-----------
+ foo_seq       |           1 |            1 | 9223372036854775807 |         1 |           1 | f         |          2 |      31 | t
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 80c5706..d1bc3d7 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -100,14 +100,14 @@ SELECT table_name, column_name, is_updatable
  ro_view17  | b             | NO
  ro_view18  | a             | NO
  ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
  ro_view19  | start_value   | NO
  ro_view19  | increment_by  | NO
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
+ ro_view19  | last_value    | NO
+ ro_view19  | log_cnt       | NO
  ro_view19  | is_called     | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 470571b..b97670f 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -242,16 +242,16 @@ CREATE FUNCTION foo_mat(int,int) RETURNS setof foo_rescan_t AS 'begin for i in $
 -- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function
 -- is on the inner path of a nestloop join
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) ON (r+i)<100;
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) ON (r+i)<100;
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( foo_sql(11,13), foo_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100;
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN generate_series(11,13) f(i) ON (r+i)<100;
@@ -262,42 +262,42 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) WITH O
 
 --invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2) WITH ORDINALITY AS f(i,s,o);
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2) WITH ORDINALITY AS f(i,s,o);
 
 -- selective rescan of multiple functions:
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_mat(10+r,13) );
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(11,11) );
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(10+r,13) );
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( foo_sql(10+r1,13), foo_mat(10+r2,13) );
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) f(i);
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..aa60bfd 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_restore_state('sequence_test'::text, '99, false');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_restore_state('sequence_test'::regclass, '99, false');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
0002-gapless_seq-v1.patchtext/x-diff; name=0002-gapless_seq-v1.patchDownload
diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..63b51c4
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,65 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-install=./tmp_check \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq
+	$(pg_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq
+	$(pg_isolation_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..3b9dbab
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,69 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+ALTER SEQUENCE test_alter_seq START 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..7029b99
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,58 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_extracols(INTERNAL)
+RETURNS INTERNAL
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_init(INTERNAL)
+RETURNS INTERNAL
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_alloc(INTERNAL)
+RETURNS INTERNAL
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_setval(INTERNAL)
+RETURNS INTERNAL
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_dump(INTERNAL)
+RETURNS CSTRING
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_restore(INTERNAL, CSTRING)
+RETURNS INTERNAL
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+DELETE FROM pg_catalog.pg_seqam
+WHERE seqamname = 'gapless';
+
+INSERT INTO pg_catalog.pg_seqam
+VALUES ('gapless',
+		'seqam_gapless_extracols'::regproc, /* extra columns for gapless sequence */
+		'seqam_local_reloptions'::regproc, /* same as local (none) */
+		'seqam_gapless_init'::regproc, /* init gapless sequence */
+		'seqam_gapless_alloc'::regproc,  /* logs and returns each value... slow */
+		'seqam_gapless_setval'::regproc, /* setval support */
+		'seqam_gapless_dump'::regproc, /* pgdump support */
+		'seqam_gapless_restore'::regproc /* pgdump support */
+		);
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..2e3a27a
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = Gapless functions
+ *
+ *------------------------------------------------------------
+ */
+extern Datum seqam_gapless_extracols(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_init(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_dump(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_restore(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_extracols);
+PG_FUNCTION_INFO_V1(seqam_gapless_init);
+PG_FUNCTION_INFO_V1(seqam_gapless_alloc);
+PG_FUNCTION_INFO_V1(seqam_gapless_setval);
+PG_FUNCTION_INFO_V1(seqam_gapless_dump);
+PG_FUNCTION_INFO_V1(seqam_gapless_restore);
+
+
+typedef struct FormGaplessSequence
+{
+	FormData_pg_sequence seq;
+	int64		tx_start_value;
+	int64		tx_last_value;
+	uint32		xid;
+} FormGaplessSequence;
+
+SeqAMColumnData seqam_gapless_cols[] = {
+	{"tx_start_value", INT8OID, -1, true},
+	{"tx_last_value", INT8OID, -1, true},
+	{"xid", XIDOID, -1, true}
+};
+#define SEQAM_GAPLESS_EXTRACOLS_CNT 3
+#define SEQAM_GAPLESS_COL_TXSTARTVAL SEQ_COL_LASTCOL + 1
+#define SEQAM_GAPLESS_COL_TXLASTVAL SEQ_COL_LASTCOL + 2
+#define SEQAM_GAPLESS_COL_XID SEQ_COL_LASTCOL + 3
+
+
+/*
+ * seqam_gapless_extracols()
+ *
+ * Get definitions for extra columns needed by a gapless sequence
+ */
+Datum
+seqam_gapless_extracols(PG_FUNCTION_ARGS)
+{
+	List *ret = NIL;
+	int i;
+
+	for (i = 0; i < SEQAM_GAPLESS_EXTRACOLS_CNT; i++)
+		ret = lappend(ret, &seqam_gapless_cols[i]);
+
+	PG_RETURN_POINTER(ret);
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ * TODO: more checks
+ */
+Datum
+seqam_gapless_init(PG_FUNCTION_ARGS)
+{
+	List *params = (List *)(PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));
+	bool is_init = PG_GETARG_BOOL(2);
+	Datum *values = (Datum *)PG_GETARG_POINTER(3);
+	bool *nulls = (bool *)PG_GETARG_POINTER(4);
+
+	ListCell   *param;
+
+	foreach(param, params)
+	{
+		DefElem    *defel = (DefElem *) lfirst(param);
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("RESTART is not supported for gapless sequences")));
+			break;
+		}
+	}
+
+	if (is_init)
+	{
+		values[SEQAM_GAPLESS_COL_TXSTARTVAL - 1] = values[SEQ_COL_STARTVAL - 1];
+		values[SEQAM_GAPLESS_COL_TXLASTVAL - 1] = values[SEQ_COL_STARTVAL - 1];
+		values[SEQAM_GAPLESS_COL_XID - 1] = UInt32GetDatum(InvalidTransactionId);
+
+		nulls[SEQAM_GAPLESS_COL_TXSTARTVAL - 1] = false;
+		nulls[SEQAM_GAPLESS_COL_TXLASTVAL - 1] = false;
+		nulls[SEQAM_GAPLESS_COL_XID - 1] = false;
+	}
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+Datum
+seqam_gapless_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	/* we ignore nreguested as gapless sequence can't do caching */
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	int64		result;
+	FormGaplessSequence *seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	while (seq->xid != local_xid && TransactionIdIsInProgress(seq->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seq->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	}
+
+	/*
+	 * Only increment if this is same transaction which consumed the sequence
+	 * previously (i.e. we are doing multiple nextval calls in same
+	 * transaction) or if the transaction that previously consumed this
+	 * sequence committed sucessfully.
+	 */
+	if (seq->xid == local_xid ||
+		TransactionIdDidCommit(seq->xid))
+	{
+		result = seq->tx_last_value;
+		(void) sequence_increment(seqrel, &result, 1, seq->seq.min_value,
+								  seq->seq.max_value, seq->seq.increment_by,
+								  seq->seq.is_cycled, true);
+	}
+	else
+	{
+		result = seq->tx_start_value;
+	}
+
+	if (seq->xid == local_xid)
+	{
+		seq->tx_last_value = result;
+	}
+	else
+	{
+		/*
+		 * Remember current xid and starting value of the sequence in this
+		 * transaction so that in case the transaction rolls back the
+		 * subsequent nextval call will know what is the real first unallocated
+		 * value.
+		 */
+		seq->tx_last_value = seq->tx_start_value = result;
+		seq->xid = local_xid;
+	}
+
+	*last = result;
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+Datum
+seqam_gapless_setval(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_dump()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ *
+ * Format is: '<last_value::bigint>'
+ *
+ * TODO: locking
+ */
+Datum
+seqam_gapless_dump(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	FormGaplessSequence *seq;
+	char		buf[19 + 1]; /* int64 + null byte */
+	char	   *result;
+
+
+	seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	pg_lltoa(seq->tx_last_value, buf);
+	result = pstrdup(buf);
+
+	PG_RETURN_CSTRING(result);
+}
+
+/*
+ * seqam_gapless_restore()
+ *
+ * Restore previously dumpred state of gapless sequence
+ *
+ * Format is: '<last_value::bigint>'
+ *
+ * TODO: locking
+*/
+Datum
+seqam_gapless_restore(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			   *state = PG_GETARG_CSTRING(2);
+	FormGaplessSequence *seq;
+	int64				last_value;
+
+	scanint8(state, false, &last_value);
+
+	seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->tx_last_value = last_value;
+	seq->xid = InvalidTransactionId;
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..326601b
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = pg_catalog
diff --git a/contrib/gapless_seq/gapless_seq.sgml b/contrib/gapless_seq/gapless_seq.sgml
new file mode 100644
index 0000000..3eb536f
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.sgml
@@ -0,0 +1,22 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless_seq" xreflabel="gapless_seq">
+ <title>gapless sequence</title>
+
+ <indexterm zone="gapless_seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence.
+  This could be very costly and is not recommended for general
+  usage except in specific applications that require this feature.
+ </para>
+
+</sect1>
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..f54cd55
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,31 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq START 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
#54Petr Jelinek
petr@2ndquadrant.com
In reply to: Petr Jelinek (#53)
1 attachment(s)
Re: Sequence Access Method WIP

On 10/12/14 03:33, Petr Jelinek wrote:

On 24/11/14 12:16, Heikki Linnakangas wrote:

About the rough edges:
- The AlterSequence is not prettiest code around as we now have to
create new relation when sequence AM is changed and I don't know how to
do that nicely
- I am not sure if I did the locking, command order and dependency
handling in AlterSequence correcly

This version does AlterSequence differently and better. Didn't attach
the gapless sequence again as that one is unchanged.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-seqam-v5.patchtext/x-diff; name=0001-seqam-v5.patchDownload
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..818da15 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+			  transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index c16b38e..35818c0 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -321,6 +321,7 @@ static bool need_initialization = true;
 static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 					int text_len, bool validate);
+static bytea *common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate);
 
 /*
  * initialize_reloptions
@@ -821,7 +822,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -854,6 +856,9 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 		case RELKIND_INDEX:
 			options = index_reloptions(amoptions, datum, false);
 			break;
+		case RELKIND_SEQUENCE:
+			options = sequence_reloptions(amoptions, datum, false);
+			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
 			break;
@@ -1299,13 +1304,31 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 /*
  * Parse options for indexes.
+ */
+bytea *
+index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for sequences.
+ */
+bytea *
+sequence_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
-bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+static bytea *
+common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..ce57f16
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,428 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAm. That is also the basic default.  Local
+ * SeqAm assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAm mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAm would also wish to control node-level caches for sequences to
+ * ensure good performance.  The CACHE option and other options may be
+ * overridden by the _alloc API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAm allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module.  The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, am_data.
+ *
+ * We currently assume that there is no persistent state held within the SeqAm,
+ * so it is safe to ALTER the access method of an object without taking any
+ * special actions, as long as we hold an AccessExclusiveLock while we do that.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAm data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*default_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_aminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Manager API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_extra_columns - get list of extra columns needed by the am
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_seqparams		- process the standard sequence parameters
+ *		seqam_dump_state	- dump sequence state (for pg_dump)
+ *		seqam_restore_state - restore sequence state (for pg_dump)
+ *
+ *		sequence_reloptions	- process reloptions - located in reloptions.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_extra_columns - get custom column list
+ */
+List *
+seqam_extra_columns(Oid seqamid)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple = NULL;
+	Form_pg_seqam seqamForm;
+	FunctionCallInfoData fcinfo;
+	Datum		ret;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	if (!RegProcedureIsValid(seqamForm->seqamextracols))
+		return NIL;
+
+	fmgr_info(seqamForm->seqamextracols, &procedure);
+
+	/*
+	 * have the seqam's proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 0, InvalidOid, NULL, NULL);
+
+	ret = FunctionCallInvoke(&fcinfo);
+
+	ReleaseSysCache(tuple);
+
+	return (List *)DatumGetPointer(ret);
+}
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+extern void
+seqam_init(Oid seqamid, List *seqparams,
+		   List *reloptions, bool is_init,
+		   Datum *values, bool *nulls)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple = NULL;
+	Form_pg_seqam seqamForm;
+	FunctionCallInfoData fcinfo;
+	char	   *validnsps[] = {NULL, NULL};
+	Datum       reloptions_transformed;
+	bytea	   *reloptions_parsed;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	fmgr_info(seqamForm->seqaminit, &procedure);
+
+	/* Allow am specific options */
+	validnsps[0] = NameStr(seqamForm->seqamname);
+
+	/*
+	 *  Parse AM-specific options, convert to text array form,
+	 *  retrieve the AM-option function and then validate.
+	 */
+	reloptions_transformed = transformRelOptions((Datum) NULL, reloptions,
+												 NULL, validnsps, false,
+												 false);
+
+	reloptions_parsed = sequence_reloptions(seqamForm->seqamreloptions,
+											reloptions_transformed, true);
+
+	/*
+	 * Have the seqam's proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqparams);
+	fcinfo.arg[1] = PointerGetDatum(reloptions_parsed);
+	fcinfo.arg[2] = BoolGetDatum(is_init);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.arg[4] = PointerGetDatum(nulls);
+	fcinfo.argnull[0] = seqparams == NULL;
+	fcinfo.argnull[1] = reloptions_parsed == NULL;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[3] = false;
+
+	FunctionCallInvoke(&fcinfo);
+
+	ReleaseSysCache(tuple);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(nrequested);
+	fcinfo.arg[3] = PointerGetDatum(last);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 3, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(new_value);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * seqam_dump_state - pg_dump support
+ */
+char *
+seqam_dump_state(Relation seqrel, SequenceHandle *seqh)
+{
+	FmgrInfo	procedure;
+	Datum		ret;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamdump, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 2, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+
+	return DatumGetCString(ret);
+}
+
+/*
+ * seqam_restore_state - restore from pg_dump
+ */
+void
+seqam_restore_state(Relation seqrel, SequenceHandle *seqh, char *state)
+{
+	FmgrInfo	procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamrestore, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = CStringGetDatum(state);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new default_sequenceam */
+bool
+check_default_seqam(char **newval, void **extra, GucSource source)
+{
+	if (**newval == '\0')
+		return true;
+
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.	Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence manager \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence manager \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+/*
+ * GetDefaultSeqAM -- get the OID of the current default sequence AM
+ *
+ * This exists to hide (and possibly optimize the use of) the
+ * default_seqam GUC variable.
+ */
+Oid
+GetDefaultSeqAM(void)
+{
+	/* Fast path for default_tablespace == "" */
+	if (default_seqam == NULL || default_seqam[0] == '\0')
+		return LOCAL_SEQAM_OID;
+
+	return get_seqam_oid(default_seqam, false);
+}
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("squence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..9cc5f11
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,405 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct FormLocalSequence
+{
+	FormData_pg_sequence seq;
+	int64		last_value;
+	int64		log_cnt;
+	bool		is_called;
+} FormLocalSequence;
+
+SeqAMColumnData seqam_local_cols[] = {
+	{"last_value", INT8OID, -1, true},
+	{"log_cnt", INT8OID, -1, true},
+	{"is_called", BOOLOID, -1, true}
+};
+#define SEQAM_LOCAL_EXTRACOLS_CNT 3
+#define SEQAM_LOCAL_COL_LASTVAL SEQ_COL_LASTCOL + 1
+#define SEQAM_LOCAL_COL_LOGCNT SEQ_COL_LASTCOL + 2
+#define SEQAM_LOCAL_COL_CALLED SEQ_COL_LASTCOL + 3
+
+/*
+ * seqam_local_extracols()
+ *
+ * Get definitions for extra columns needed by a local sequence
+ */
+Datum
+seqam_local_extracols(PG_FUNCTION_ARGS)
+{
+	List *ret = NIL;
+	int i;
+
+	for (i = 0; i < SEQAM_LOCAL_EXTRACOLS_CNT; i++)
+		ret = lappend(ret, &seqam_local_cols[i]);
+
+	PG_RETURN_POINTER(ret);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum       reloptions = PG_GETARG_DATUM(0);
+	bool        validate = PG_GETARG_BOOL(1);
+	bytea      *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	List *params = (List *)(PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));
+	bool is_init = PG_GETARG_BOOL(2);
+	Datum *values = (Datum *)PG_GETARG_POINTER(3);
+	bool *nulls = (bool *)PG_GETARG_POINTER(4);
+
+	DefElem    *restart_value = NULL;
+	int64		last_value,
+				min_value,
+				max_value;
+	ListCell   *param;
+
+	foreach(param, params)
+	{
+		DefElem    *defel = (DefElem *) lfirst(param);
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			/* The redundacy check is done in the main sequence code. */
+			restart_value = defel;
+			break;
+		}
+	}
+
+	/* RESTART [WITH] */
+	if (restart_value != NULL)
+	{
+		Datum		last_value;
+
+		if (restart_value->arg != NULL)
+			last_value = defGetInt64(restart_value);
+		else
+			last_value = values[SEQ_COL_STARTVAL - 1];
+
+		values[SEQAM_LOCAL_COL_LASTVAL - 1] = last_value;
+		values[SEQAM_LOCAL_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+	else if (is_init)
+	{
+		values[SEQAM_LOCAL_COL_LASTVAL - 1] = values[SEQ_COL_STARTVAL - 1];
+		values[SEQAM_LOCAL_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+
+	last_value = DatumGetInt64(values[SEQAM_LOCAL_COL_LASTVAL - 1]);
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+
+	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
+	if (last_value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, last_value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
+					  bufs, bufm)));
+	}
+	if (last_value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, last_value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
+				   bufs, bufm)));
+	}
+
+	/* log_cnt */
+	values[SEQAM_LOCAL_COL_LOGCNT - 1] = Int64GetDatum(0);
+
+	nulls[SEQAM_LOCAL_COL_LASTVAL - 1] = false;
+	nulls[SEQAM_LOCAL_COL_LOGCNT - 1] = false;
+	nulls[SEQAM_LOCAL_COL_CALLED - 1] = false;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormLocalSequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				logit = false;
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	next = result = seq->last_value;
+	incby = seq->seq.increment_by;
+	maxv = seq->seq.max_value;
+	minv = seq->seq.min_value;
+	is_cycled = seq->seq.is_cycled;
+	fetch = nrequested;
+	log = seq->log_cnt;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!seq->is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !seq->is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (seq->is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	START_CRIT_SECTION();
+
+	/* Log our cached data. */
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		seq->log_cnt = 0;
+
+		sequence_save_tuple(seqh, NULL, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	seq->log_cnt = log;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, false);
+
+	END_CRIT_SECTION();
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    next = PG_GETARG_INT64(2);
+	FormLocalSequence *seq;
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	seq->log_cnt = 0;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_dump()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ *
+ * Format is: '<last_value::bigint>, <is_called::boolean>'
+ */
+Datum
+seqam_local_dump(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	FormLocalSequence  *seq;
+	char		buf[19 + 1 + 1 + 1 + 1]; /* int64 + colon + space + t/f + null byte */
+	char	   *result;
+
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	pg_lltoa(seq->last_value, buf);
+	strcat(buf, seq->is_called ? ", t" : ", f");
+	result = pstrdup(buf);
+
+	PG_RETURN_CSTRING(result);
+}
+
+/*
+ * seqam_local_restore()
+ *
+ * Restore previously dumpred state of local sequence (used by pg_dump)
+ *
+ * Format is: '<last_value::bigint>, <is_called::boolean>'
+*/
+Datum
+seqam_local_restore(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			   *state = PG_GETARG_CSTRING(2);
+	FormLocalSequence  *seq;
+	int64				last_value;
+	bool				is_called;
+	char			   *buf;
+	char			   *ptr = state;
+	int					len;
+
+	/* Check that we can find comma. */
+	while (*ptr != ',' && *ptr != '\0')
+		ptr++;
+
+	if (*ptr == '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for local sequence state: \"%s\"",
+						state)));
+
+	/*
+	 * Copy the part of the string before comma into work buffer
+	 * for scanint8, it will handle leading/trailing whitespace correctly.
+	 */
+	len = ptr - state;
+	buf = palloc(len + 1);
+	memcpy(buf, state, len);
+	buf[len] = '\0';
+	scanint8(buf, false, &last_value);
+
+	/* Skip leading/trailing whitespace for parse_bool_with_len. */
+	ptr++;
+	while (isspace((unsigned char) *ptr))
+		ptr++;
+
+	len = strlen(ptr);
+	while (len > 0 && isspace((unsigned char) ptr[len - 1]))
+		len--;
+
+	/* Parse out the boolean */
+	if (!parse_bool_with_len(ptr, len, &is_called))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for local sequence state: \"%s\"",
+						state)));
+
+	pfree(buf);
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->last_value = last_value;
+	seq->is_called = is_called;
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a403c64..147c571 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index ba5b938..6689915 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,15 +14,21 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -35,19 +41,13 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -80,6 +80,14 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTuple	tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -90,13 +98,124 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static Oid get_new_seqam_oid(Oid oldAM, char *accessMethod);
+
+
+/*
+ * Build template column definition for a sequence relation.
+ */
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_from_type = false;
+	coldef->storage = 0;
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Create the relation for sequence.
+ */
+static Oid
+DefineSequenceRelation(RangeVar *rel, List *cols,
+					   bool if_not_exists, Oid ownerId)
+{
+	CreateStmt *stmt = makeNode(CreateStmt);
+
+	stmt->relation = rel;
+	stmt->tableElts = cols;
+	stmt->inhRelations = NIL;
+	stmt->constraints = NIL;
+	stmt->options = NIL;
+	stmt->oncommit = ONCOMMIT_NOOP;
+	stmt->tablespacename = NULL;
+	stmt->if_not_exists = if_not_exists;
+
+	return DefineRelation(stmt, RELKIND_SEQUENCE, ownerId);
+}
+
+/*
+ * Add additional sequence am columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(List *amcols)
+{
+	List	   *seqcols;
+	ListCell   *amcol;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		coldef->is_not_null = true;
+
+		switch (colid)
+		{
+			case SEQ_COL_NAME:
+				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
+				coldef->colname = "sequence_name";
+				break;
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+		}
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	foreach(amcol, amcols)
+	{
+		SeqAMColumn	amcolumn = lfirst(amcol);
+
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		coldef->typeName = makeTypeNameFromOid(amcolumn->coltyp,
+											   amcolumn->coltypmod);
+		coldef->colname = amcolumn->colname;
+		coldef->is_not_null = amcolumn->colnotnull;
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	return seqcols;
+}
 
 
 /*
@@ -108,15 +227,17 @@ DefineSequence(CreateSeqStmt *seq)
 {
 	FormData_pg_sequence new;
 	List	   *owned_by;
-	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	Relation	rel;
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
+	Datum	   *value;
+	bool	   *null;
+	int			colid;
 	NameData	name;
+	List	   *seqcols,
+			   *amcols;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -142,104 +263,75 @@ DefineSequence(CreateSeqStmt *seq)
 		}
 	}
 
-	/* Check and set all option values */
+	/* Check and set all param values */
 	init_params(seq->options, true, &new, &owned_by);
 
+	/* Build column definitions. */
+	seqamid = get_new_seqam_oid(InvalidOid, seq->accessMethod);
+	amcols = seqam_extra_columns(seqamid);
+
+	seqcols = BuildSeqColumnDefList(amcols);
+
+	value = palloc0(list_length(seqcols) * sizeof(Datum));
+	null = palloc0(list_length(seqcols) * sizeof(bool));
+
 	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
+	 * Fill value[] and null[] for the tuple.
 	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
 	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
+		null[colid - 1] = false;
 
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
+		switch (colid)
 		{
 			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
 				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
+				value[colid - 1] = NameGetDatum(&name);
 				break;
 			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
+				value[colid - 1] = Int64GetDatumFast(new.start_value);
 				break;
 			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
+				value[colid - 1] = Int64GetDatumFast(new.increment_by);
 				break;
 			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
+				value[colid - 1] = Int64GetDatumFast(new.max_value);
 				break;
 			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
+				value[colid - 1] = Int64GetDatumFast(new.min_value);
 				break;
 			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
+				value[colid - 1] = Int64GetDatumFast(new.cache_value);
 				break;
 			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
+				value[colid - 1] = BoolGetDatum(new.is_cycled);
 				break;
 		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
 
-	stmt->relation = seq->sequence;
-	stmt->inhRelations = NIL;
-	stmt->constraints = NIL;
-	stmt->options = NIL;
-	stmt->oncommit = ONCOMMIT_NOOP;
-	stmt->tablespacename = NULL;
-	stmt->if_not_exists = seq->if_not_exists;
+	/* Let AM fill the value[] and null[] for the tuple as well. */
+	seqam_init(seqamid, seq->options, seq->amoptions, true,
+			   value, null);
 
-	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
+	/* Create the sequence relation */
+	seqoid = DefineSequenceRelation(seq->sequence, seqcols,
+									seq->if_not_exists, seq->ownerId);
 	Assert(seqoid != InvalidOid);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
 	/* now initialize the sequence's data */
+	tupDesc = RelationGetDescr(rel);
 	tuple = heap_form_tuple(tupDesc, value, null);
+
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -266,56 +358,68 @@ DefineSequence(CreateSeqStmt *seq)
 void
 ResetSequence(Oid seq_relid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	HeapTuple	newtup;
+	Relation	seqrel;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle  *seqh;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	seqh = sequence_open(seq_relid);
+	tuple = sequence_read_tuple(seqh);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
-	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	seqrel = seqh->rel;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	memset(replaces, true, tupDesc->natts * sizeof(bool));
+
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+
+	seqam_init(seqrel->rd_rel->relam,
+			   list_make1(makeDefElem("restart", NULL)), NULL, false,
+			   values, nulls);
+
+	newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqh->rel, seqh->rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqh->rel, newtup);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh->elm->cached = seqh->elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(seqh);
 }
 
 /*
@@ -368,23 +472,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -400,16 +488,23 @@ Oid
 AlterSequence(AlterSeqStmt *stmt)
 {
 	Oid			relid;
+	Oid			oldamid;
+	Oid			seqamid;
 	SeqTable	elm;
+	HeapTuple	tuple;
+	HeapTuple	newtup;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle *seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
+	relid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
 	if (relid == InvalidOid)
 	{
 		ereport(NOTICE,
@@ -418,7 +513,9 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidOid;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
+	elm = seqh->elm;
+	seqrel = seqh->rel;
 
 	/* allow ALTER to sequence owner only */
 	if (!pg_class_ownercheck(relid, GetUserId()))
@@ -426,48 +523,98 @@ AlterSequence(AlterSeqStmt *stmt)
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+	oldamid = seqrel->rd_rel->relam;
+	seqamid = get_new_seqam_oid(seqrel->rd_rel->relam, stmt->accessMethod);
 
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
+	/*
+	 * If we are changing sequence AM, we need to replace
+	 * the sequence relation.
+	 */
+	if (seqamid != oldamid)
 	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		List   *oldamcols = seqam_extra_columns(oldamid);
+		List   *newamcols = seqam_extra_columns(seqamid);
+		ListCell *amcol;
+		List   *atcmds = NIL;
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		sequence_release_tuple(seqh);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		foreach(amcol, oldamcols)
+		{
+			SeqAMColumn	amcolumn = lfirst(amcol);
+			AlterTableCmd *cmd = makeNode(AlterTableCmd);
+
+			cmd->subtype = AT_DropColumnFromSequence;
+			cmd->name = amcolumn->colname;
+			cmd->behavior = DROP_RESTRICT;
+			cmd->missing_ok = TRUE;
+			atcmds = lappend(atcmds, cmd);
+		}
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		foreach(amcol, newamcols)
+		{
+			SeqAMColumn	amcolumn = lfirst(amcol);
+			ColumnDef  *coldef = makeSeqColumnDef();
+			AlterTableCmd *cmd = makeNode(AlterTableCmd);
+
+			coldef->typeName = makeTypeNameFromOid(amcolumn->coltyp,
+												   amcolumn->coltypmod);
+			coldef->colname = amcolumn->colname;
+			coldef->is_not_null = amcolumn->colnotnull;
+
+			cmd->subtype = AT_AddColumnToSequence;
+			cmd->def = (Node *) coldef;
+			atcmds = lappend(atcmds, cmd);
+		}
 
-		PageSetLSN(page, recptr);
-	}
+		AlterTableInternal(relid, atcmds, false);
 
-	END_CRIT_SECTION();
+		/* Let sequence AM update the tuple. */
+		tupDesc = RelationGetDescr(seqrel);
 
-	UnlockReleaseBuffer(buf);
+		values = (Datum *) repalloc(values, tupDesc->natts * sizeof(Datum));
+		nulls = (bool *) repalloc(nulls, tupDesc->natts * sizeof(bool));
+
+		seqam_init(seqamid, stmt->options, stmt->amoptions, seqamid != oldamid,
+				   values, nulls);
+
+		newtup = heap_form_tuple(tupDesc, values, nulls);
+		fill_seq_with_data(seqh->rel, newtup);
+
+		seqrel_update_relam(relid, seqamid);
+	}
+	else
+	{
+		/* Let sequence AM update the tuple. */
+		replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+		memset(replaces, true, tupDesc->natts * sizeof(bool));
+		seqam_init(seqamid, stmt->options, stmt->amoptions, seqamid != oldamid,
+				   values, nulls);
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
+		sequence_save_tuple(seqh, newtup, true);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	elm->cached = elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
@@ -475,7 +622,7 @@ AlterSequence(AlterSeqStmt *stmt)
 
 	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(seqh);
 
 	return relid;
 }
@@ -516,29 +663,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle *seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
+	elm = seqh->elm;
+	seqrel = seqh->rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -556,121 +698,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
 
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -679,261 +715,119 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_close(seqh);
 
 	return result;
 }
 
+
 Datum
 currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle *seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh->elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
-
-	if (!elm->last_valid)
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
-
-	result = elm->last;
-
-	relation_close(seqrel, NoLock);
-
-	PG_RETURN_INT64(result);
-}
-
-Datum
-lastval(PG_FUNCTION_ARGS)
-{
-	Relation	seqrel;
-	int64		result;
-
-	if (last_used_seq == NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("lastval is not yet defined in this session")));
-
-	/* Someone may have dropped the sequence since the last nextval() */
-	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("lastval is not yet defined in this session")));
-
-	seqrel = open_share_lock(last_used_seq);
-
-	/* nextval() must have already been called for this sequence */
-	Assert(last_used_seq->last_valid);
-
-	if (pg_class_aclcheck(last_used_seq->relid, GetUserId(),
-						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
-
-	result = last_used_seq->last;
-	relation_close(seqrel, NoLock);
-
-	PG_RETURN_INT64(result);
-}
-
-/*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
- */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
-{
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-
-	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
-
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh->rel))));
 
-	/* read-only transactions may only modify temp sequences */
-	if (!seqrel->rd_islocaltemp)
-		PreventCommandIfReadOnly("setval()");
-
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
+	if (!seqh->elm->last_valid)
 		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
-
-	/* In any case, forget any future cached numbers */
-	elm->cached = elm->last;
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
+						RelationGetRelationName(seqh->rel))));
 
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
+	result = seqh->elm->last;
 
-	MarkBufferDirty(buf);
+	sequence_close(seqh);
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+	PG_RETURN_INT64(result);
+}
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+Datum
+lastval(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel;
+	int64		result;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+	if (last_used_seq == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("lastval is not yet defined in this session")));
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+	/* Someone may have dropped the sequence since the last nextval() */
+	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("lastval is not yet defined in this session")));
 
-		PageSetLSN(page, recptr);
-	}
+	seqrel = open_share_lock(last_used_seq);
 
-	END_CRIT_SECTION();
+	/* nextval() must have already been called for this sequence */
+	Assert(last_used_seq->last_valid);
 
-	UnlockReleaseBuffer(buf);
+	if (pg_class_aclcheck(last_used_seq->relid, GetUserId(),
+						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+						RelationGetRelationName(seqrel))));
 
+	result = last_used_seq->last;
 	relation_close(seqrel, NoLock);
+
+	PG_RETURN_INT64(result);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the setval procedure.
  */
 Datum
 setval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle *seqh;
 
-	do_setval(relid, next, true);
+	/* open and AccessShareLock sequence */
+	seqh = sequence_open(relid);
+	elm = seqh->elm;
+	seqrel = seqh->rel;
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+						RelationGetRelationName(seqrel))));
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-	do_setval(relid, next, iscalled);
+	seqam_setval(seqrel, seqh, next);
+
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
+
+	sequence_close(seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -993,21 +887,21 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+SequenceHandle *
+sequence_open(Oid seq_relid)
 {
-	SeqTable	elm;
-	Relation	seqrel;
-	bool		found;
+	SequenceHandle *seqh;
+	SeqTable		elm;
+	Relation		seqrel;
+	bool			found;
 
 	/* Find or create a hash table entry for this sequence */
 	if (seqhashtab == NULL)
 		create_seq_hashtable();
 
-	elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);
+	elm = (SeqTable) hash_search(seqhashtab, &seq_relid, HASH_ENTER, &found);
 
 	/*
 	 * Initialize the new hash table entry if it did not exist already.
@@ -1048,45 +942,60 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 		elm->cached = elm->last;
 	}
 
+	seqh = (SequenceHandle *) palloc(sizeof(SequenceHandle));
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup = NULL;
+
+	return seqh;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	sequence_release_tuple(seqh);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
+	Form_pg_sequence seq_form;
+
+	if (HeapTupleIsValid(seqh->tup))
+		return seqh->tup;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	seqh->tup = palloc0(HEAPTUPLESIZE);
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of seqh->tup */
+	seqh->tup->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup->t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1096,33 +1005,116 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup->t_data) != InvalidTransactionId)
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		HeapTupleHeaderSetXmax(seqh->tup->t_data, InvalidTransactionId);
+		seqh->tup->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
 	}
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	seq_form = (Form_pg_sequence) GETSTRUCT(seqh->tup);
 
 	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	seqh->elm->increment = seq_form->increment_by;
+
+	return seqh->tup;
+}
+
+/*
+ * Update the page, optionally do WAL logging of the tuple
+ */
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal)
+{
+	Page	page;
+
+	Assert(HeapTupleIsValid(seqh->tup));
+
+	page = BufferGetPage(seqh->buf);
+
+	/* Only do tuple replacement if we got valid tuple on input. */
+	if (HeapTupleIsValid(newtup))
+	{
+		Page	temppage;
+
+		temppage = PageGetTempPageCopySpecial(page);
+
+		if (PageAddItem(temppage, (Item) newtup->t_data, newtup->t_len,
+						FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+			elog(PANIC, "sequence_save_tuple: failed to add item to page");
+
+		PageSetLSN(temppage, PageGetLSN(page));
+
+		START_CRIT_SECTION();
 
-	return seq;
+		PageRestoreTempPage(temppage, page);
+		seqh->tup = newtup;
+		MarkBufferDirty(seqh->buf);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, newtup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+	else
+	{
+		START_CRIT_SECTION();
+
+		MarkBufferDirty(seqh->buf);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, seqh->tup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+}
+
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (HeapTupleIsValid(seqh->tup))
+	{
+		pfree(seqh->tup);
+		seqh->tup = NULL;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
+	{
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
+	}
+}
+
+/*
+ * Returns true if sequence was not WAL logged since checkpoint
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr = GetRedoRecPtr();
+
+	Assert(BufferIsValid(seqh->buf));
+
+	page = BufferGetPage(seqh->buf);
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1132,13 +1124,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1209,13 +1201,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1224,7 +1209,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1234,7 +1218,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1243,7 +1226,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1251,14 +1233,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1266,7 +1246,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1320,48 +1299,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1376,14 +1313,13 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1399,8 +1335,7 @@ process_owned_by(Relation seqrel, List *owned_by)
 
 	nnames = list_length(owned_by);
 	Assert(nnames > 0);
-	if (nnames == 1)
-	{
+	if (nnames == 1)	{
 		/* Must be OWNED BY NONE */
 		if (strcmp(strVal(linitial(owned_by)), "none") != 0)
 			ereport(ERROR,
@@ -1487,20 +1422,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  *seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh->rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1518,7 +1450,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1526,12 +1458,99 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_close(seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_dump_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *result;
+	SequenceHandle  *seqh;
+
+	seqh = sequence_open(relid);
+
+	result = seqam_dump_state(seqh->rel, seqh);
+
+	sequence_close(seqh);
+
+	PG_RETURN_CSTRING(result);
+}
+
+Datum
+pg_sequence_restore_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *state = PG_GETARG_CSTRING(1);
+	SequenceHandle  *seqh;
+
+	seqh = sequence_open(relid);
+
+	seqam_restore_state(seqh->rel, seqh, state);
+
+	sequence_close(seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+		CatalogUpdateIndexes(rd, ctup);
+	}
+
+	heap_freetuple(ctup);
+	heap_close(rd, RowExclusiveLock);
+	CommandCounterIncrement();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1583,6 +1602,7 @@ seq_redo(XLogReaderState *record)
 	pfree(localpage);
 }
 
+
 /*
  * Flush cached sequence information.
  */
@@ -1597,3 +1617,92 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+static Oid
+get_new_seqam_oid(Oid oldAM, char *accessMethod)
+{
+
+	if (oldAM && accessMethod == NULL)
+		return oldAM;
+	else if (accessMethod == NULL || strcmp(accessMethod, DEFAULT_SEQAM) == 0)
+		return GetDefaultSeqAM();
+	else
+		return get_seqam_oid(accessMethod, false);
+}
+
+
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64 next = *value;
+	int64 rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1e737a0..f15c0c4 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -265,6 +265,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -2838,6 +2839,8 @@ AlterTableGetLockLevel(List *cmds)
 				 */
 			case AT_DropColumn:	/* change visible to SELECT */
 			case AT_AddColumnToView:	/* CREATE VIEW */
+			case AT_AddColumnToSequence:	/* ALTER SEQUENCE USING */
+			case AT_DropColumnFromSequence:	/* ALTER SEQUENCE USING */
 			case AT_DropOids:	/* calls AT_DropColumn */
 			case AT_EnableAlwaysRule:	/* may change SELECT rules */
 			case AT_EnableReplicaRule:	/* may change SELECT rules */
@@ -3097,6 +3100,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			/* Recursion occurs during execution phase */
 			pass = AT_PASS_ADD_COL;
 			break;
+		case AT_AddColumnToSequence:	/* add column via ALTER SEQUENCE
+										   USING */
+			ATSimplePermissions(rel, ATT_SEQUENCE);
+			ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
+			/* This command never recurses */
+			pass = AT_PASS_MISC;
+			break;
 		case AT_ColumnDefault:	/* ALTER COLUMN DEFAULT */
 
 			/*
@@ -3147,6 +3157,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			/* Recursion occurs during execution phase */
 			pass = AT_PASS_DROP;
 			break;
+		case AT_DropColumnFromSequence:	/* drop column via ALTER SEQUENCE
+										   USING */
+			ATSimplePermissions(rel, ATT_SEQUENCE);
+			ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
+			/* This command never recurses */
+			pass = AT_PASS_MISC;
+			break;
 		case AT_AddIndex:		/* ADD INDEX */
 			ATSimplePermissions(rel, ATT_TABLE);
 			/* This command never recurses */
@@ -3399,6 +3416,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		case AT_AddColumn:		/* ADD COLUMN */
 		case AT_AddColumnToView:		/* add column via CREATE OR REPLACE
 										 * VIEW */
+		case AT_AddColumnToSequence:	/* add column via ALTER SEQUENCE
+										   USING */
 			ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
 							false, false, false, lockmode);
 			break;
@@ -3428,6 +3447,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
 			break;
 		case AT_DropColumn:		/* DROP COLUMN */
+		case AT_DropColumnFromSequence:		/* ALTER SEQUENCE USING */
 			ATExecDropColumn(wqueue, rel, cmd->name,
 					 cmd->behavior, false, false, cmd->missing_ok, lockmode);
 			break;
@@ -4211,6 +4231,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4275,6 +4298,9 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a foreign table");
 			break;
+		case ATT_SEQUENCE:
+			msg = _("\"%s\" is not a sequence");
+			break;
 		default:
 			/* shouldn't get here, add all necessary cases above */
 			msg = _("\"%s\" is of the wrong type");
@@ -9046,6 +9072,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 		case RELKIND_INDEX:
 			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
+		case RELKIND_SEQUENCE:
+			(void) sequence_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6b1bf7b..c9f7f8e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3361,7 +3361,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3374,7 +3376,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d5db71d..90e765c 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1567,7 +1567,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1578,7 +1580,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b5009b..56ff30b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3503,7 +3504,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = DEFAULT_SEQAM;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3525,7 +3552,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3533,11 +3584,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3596,7 +3670,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 7c1939f..5994d18 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -399,6 +399,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index eca3f97..0aa4cf2 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 79244e5..d003a28 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1055,11 +1057,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1535,6 +1540,39 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for an index relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	MemoryContext	indexcxt;
+	Form_pg_seqam	amform;
+
+	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+									 RelationGetRelationName(rel),
+									 ALLOCSET_SMALL_MINSIZE,
+									 ALLOCSET_SMALL_INITSIZE,
+									 ALLOCSET_SMALL_MAXSIZE);
+	rel->rd_indexcxt = indexcxt;
+
+	rel->rd_aminfo = (RelationAmInfo *)
+		MemoryContextAllocZero(rel->rd_indexcxt,
+							   sizeof(RelationAmInfo));
+
+	/*
+	 * Make a copy of the pg_am entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+}
 
 /*
  *		formrdesc
@@ -4792,6 +4830,22 @@ load_relcache_init_file(bool shared)
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
+		else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+		{
+			MemoryContext indexcxt;
+			Assert(!rel->rd_isnailed);
+			Assert(false);
+
+			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+											 RelationGetRelationName(rel),
+											 ALLOCSET_SMALL_MINSIZE,
+											 ALLOCSET_SMALL_INITSIZE,
+											 ALLOCSET_SMALL_MAXSIZE);
+			rel->rd_indexcxt = indexcxt;
+			/* set up zeroed fmgr-info vectors */
+			rel->rd_aminfo = (RelationAmInfo *)
+				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+		}
 		else
 		{
 			/* Count nailed rels to ensure we have 'em all */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 94d951c..66deaa6 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAmNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_amname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAmOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b1bff7f..51c8982 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2788,6 +2789,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"default_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence am for any new sequences."),
+			gettext_noop("An empty string selects the 'local' sequence am."),
+			GUC_IS_NAME | GUC_NOT_IN_SAMPLE
+		},
+		&default_seqam,
+		"",
+		check_default_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4175ddc..f4d10e0 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14567,7 +14567,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = "local";
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14637,15 +14638,41 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	}
 #endif
 
-	startv = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
+	startv = pg_strdup(PQgetvalue(res, 0, 1));
+	incby = pg_strdup(PQgetvalue(res, 0, 2));
 	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
+		maxv = pg_strdup(PQgetvalue(res, 0, 3));
 	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
+		minv = pg_strdup(PQgetvalue(res, 0, 4));
+	cache = pg_strdup(PQgetvalue(res, 0, 5));
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
+	PQclear(res);
+
+	res = ExecuteSqlQuery(fout, "SELECT EXISTS(SELECT 1 "
+								"FROM pg_catalog.pg_class c, "
+								"pg_catalog.pg_namespace n "
+								"WHERE n.oid = c.relnamespace "
+								"AND nspname = 'pg_catalog'"
+								"AND c.relname = 'pg_seqam' "
+								"AND c.relkind = 'r');",
+						  PGRES_TUPLES_OK);
+	if (strcmp(PQgetvalue(res, 0, 0), "t") == 0)
+	{
+		PQclear(res);
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		amname = pg_strdup(PQgetvalue(res, 0, 0));
+	}
+
+	PQclear(res);
+
 	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
@@ -14687,6 +14714,7 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14753,8 +14781,6 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
-	PQclear(res);
-
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(delqry);
 	destroyPQExpBuffer(labelq);
@@ -14769,17 +14795,30 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
+	char	   *state;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	res = ExecuteSqlQuery(fout, "SELECT EXISTS(SELECT 1 "
+								"FROM pg_catalog.pg_proc p, "
+								"pg_catalog.pg_namespace n "
+								"WHERE n.oid = p.pronamespace "
+								"AND nspname = 'pg_catalog'"
+								"AND p.proname = 'pg_sequence_dump_state');",
+						  PGRES_TUPLES_OK);
+
+	if (strcmp(PQgetvalue(res, 0, 0), "t") == 0)
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(last_value::text || ', ' || is_called::text) FROM %s",
+						  fmtId(tbinfo->dobj.name));
+	else
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_dump_state(%s))",
+						  fmtId(tbinfo->dobj.name));
 
+	PQclear(res);
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
 	if (PQntuples(res) != 1)
@@ -14791,14 +14830,12 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+	state = PQgetvalue(res, 0, 0);
 
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+	appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_restore_state(");
 	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+	appendPQExpBuffer(query, ", %s);\n", state);
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 5a9ceca..6dc428d 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1372,30 +1372,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1439,6 +1415,14 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S' && !verbose)
+		appendPQExpBufferStr(&buf, " AND attname IN ('sequence_name', "
+							 "'start_value', 'increment_by', 'max_value', "
+							 "'min_value', 'cache_value', 'is_cycled')");
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
 	res = PSQLexec(buf.data);
@@ -1446,6 +1430,39 @@ describeOneTableDetails(const char *schemaname,
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1757,6 +1774,29 @@ describeOneTableDetails(const char *schemaname,
 		/* Footer information about a sequence */
 		PGresult   *result = NULL;
 
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
+		/*
+		 * If we get no rows back, don't show anything (obviously). We should
+		 * never get more than one row back, but if we do, just ignore it and
+		 * don't print anything.
+		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
+		PQclear(result);
+
 		/* Get the column that owns this sequence */
 		printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
 						  "\n   pg_catalog.quote_ident(relname) || '.' ||"
@@ -1774,6 +1814,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1783,11 +1825,6 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
-		/*
-		 * If we get no rows back, don't show anything (obviously). We should
-		 * never get more than one row back, but if we do, just ignore it and
-		 * don't print anything.
-		 */
 		PQclear(result);
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a538830..bf4cafa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -46,8 +46,9 @@ typedef enum relopt_kind
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
 	RELOPT_KIND_BRIN = (1 << 10),
+	RELOPT_KIND_SEQUENCE = (1 << 11),
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -272,6 +273,8 @@ extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
 extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
 				 bool validate);
+extern bytea *sequence_reloptions(RegProcedure amoptions, Datum reloptions,
+				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..c4f6f73
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef struct SeqAMColumnData
+{
+	char   *colname;
+	Oid		coltyp;
+	int32	coltypmod;
+	bool	colnotnull;
+} SeqAMColumnData;
+
+typedef SeqAMColumnData *SeqAMColumn;
+
+extern char *default_seqam;
+
+extern Oid GetDefaultSeqAM(void);
+
+extern List *seqam_extra_columns(Oid seqamid);
+extern void seqam_init(Oid seqamid, List *seqparams,
+					   List *reloptions, bool is_init,
+					   Datum *values, bool *nulls);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern char *seqam_dump_state(Relation seqrel, SequenceHandle *seqh);
+extern void seqam_restore_state(Relation seqrel, SequenceHandle *seqh, char *state);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern SequenceHandle *sequence_open(Oid seq_relid);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+
+extern Datum seqam_local_extracols(PG_FUNCTION_ARGS);
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_dump(PG_FUNCTION_ARGS);
+extern Datum seqam_local_restore(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index bde1a84..2daffe4 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAmNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAmOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 910cfc6..a96347f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1814,10 +1814,12 @@ DATA(insert OID = 1575 (  currval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 20
 DESCR("sequence current value");
 DATA(insert OID = 1576 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 20 "2205 20" _null_ _null_ _null_ _null_  setval_oid _null_ _null_ _null_ ));
 DESCR("set sequence value");
-DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ ));
-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_ ));
+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_ ));
+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_ ));
 DESCR("I/O");
@@ -5042,6 +5044,22 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6022 (  seqam_local_extracols	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ seqam_local_extracols _null_ _null_ _null_ ));
+DESCR("Get local SequenceAM extra columns");
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 2281 "2281 16" _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "2281 17 16 2281 2281" _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_dump	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 2281" _null_ _null_ _null_ _null_ seqam_local_dump _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_restore	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 17" _null_ _null_ _null_ _null_ seqam_local_restore _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 DESCR("list objects dropped by the current command");
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..e08c935
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,78 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	32
+
+CATALOG(pg_seqam,32)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamextracols;		/* get extra columns this am stores in the sequence relation */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamdump;			/* dump state, used by pg_dump */
+	regproc		seqamrestore;		/* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						8
+#define Anum_pg_seqam_amname				1
+#define Anum_pg_seqam_amextracols			2
+#define Anum_pg_seqam_amreloptions			3
+#define Anum_pg_seqam_aminit				4
+#define Anum_pg_seqam_amalloc				5
+#define Anum_pg_seqam_amsetval				6
+#define Anum_pg_seqam_amdump				7
+#define Anum_pg_seqam_amrestore				8
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 2 (  local		seqam_local_extracols seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_dump seqam_local_restore));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 2
+
+#define DEFAULT_SEQAM	""
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 386f1e6..8688620 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -23,15 +23,12 @@
 typedef struct FormData_pg_sequence
 {
 	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,18 +38,14 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  */
 
 #define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
-
+#define SEQ_COL_STARTVAL		2
+#define SEQ_COL_INCBY			3
+#define SEQ_COL_MAXVALUE		4
+#define SEQ_COL_MINVALUE		5
+#define SEQ_COL_CACHE			6
+#define SEQ_COL_CYCLE			7
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_CYCLE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -67,10 +60,11 @@ extern Datum nextval(PG_FUNCTION_ARGS);
 extern Datum nextval_oid(PG_FUNCTION_ARGS);
 extern Datum currval_oid(PG_FUNCTION_ARGS);
 extern Datum setval_oid(PG_FUNCTION_ARGS);
-extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_dump_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_restore_state(PG_FUNCTION_ARGS);
 
 extern Oid	DefineSequence(CreateSeqStmt *stmt);
 extern Oid	AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5eaa435..fb355dc 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1283,6 +1283,7 @@ typedef enum AlterTableType
 	AT_AddColumn,				/* add column */
 	AT_AddColumnRecurse,		/* internal to commands/tablecmds.c */
 	AT_AddColumnToView,			/* implicitly via CREATE OR REPLACE VIEW */
+	AT_AddColumnToSequence,		/* implicitly via ALTER SEQUENCE USING */
 	AT_ColumnDefault,			/* alter column default */
 	AT_DropNotNull,				/* alter column drop not null */
 	AT_SetNotNull,				/* alter column set not null */
@@ -1292,6 +1293,7 @@ typedef enum AlterTableType
 	AT_SetStorage,				/* alter column set storage */
 	AT_DropColumn,				/* drop column */
 	AT_DropColumnRecurse,		/* internal to commands/tablecmds.c */
+	AT_DropColumnFromSequence,	/* implicitly via ALTER SEQUENCE USING */
 	AT_AddIndex,				/* add index */
 	AT_ReAddIndex,				/* internal to commands/tablecmds.c */
 	AT_AddConstraint,			/* add constraint */
@@ -2011,8 +2013,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2020,8 +2024,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 80813d2..507044e 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -404,6 +404,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_default_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 48ebf59..2998688 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -48,10 +49,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
+ * functions, defined by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+	/* pg_am only */
 	FmgrInfo	aminsert;
 	FmgrInfo	ambeginscan;
 	FmgrInfo	amgettuple;
@@ -61,6 +64,14 @@ typedef struct RelationAmInfo
 	FmgrInfo	ammarkpos;
 	FmgrInfo	amrestrpos;
 	FmgrInfo	amcanreturn;
+	FmgrInfo	amcostestimate;
+
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+
+	/* Common */
+	FmgrInfo	amoptions;
 } RelationAmInfo;
 
 
@@ -131,23 +142,25 @@ typedef struct RelationData
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+
 	/*
-	 * index access support info (used only for an index relation)
+	 * Access support info (used only for index or sequence relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
 	 * arrays are indexed by support function number, which is a sufficient
 	 * identifier given that restriction.
 	 *
-	 * Note: rd_amcache is available for index AMs to cache private data about
-	 * an index.  This must be just a cache since it may get reset at any time
+	 * Note: rd_amcache is available for AMs to cache private data about
+	 * an object.  This must be just a cache since it may get reset at any time
 	 * (in particular, it will get reset by a relcache inval message for the
 	 * index).  If used, it must point to a single memory chunk palloc'd in
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
-	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am */
+	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am or pg_seqam */
 	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
@@ -158,7 +171,7 @@ typedef struct RelationData
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
-	void	   *rd_amcache;		/* available for use by index AM */
+	void	   *rd_amcache;		/* available for use by AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
 
 	/*
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f97229f..2810a8d 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 7991e99..6b6493e 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -719,10 +719,10 @@ CREATE FUNCTION foo_mat(int,int) RETURNS setof foo_rescan_t AS 'begin for i in $
 --invokes ExecReScanFunctionScan - all these cases should materialize the function only once
 -- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function
 -- is on the inner path of a nestloop join
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) ON (r+i)<100;
@@ -739,10 +739,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) ON (r+i)<100;
  3 | 13 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
@@ -759,10 +759,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) WITH ORDINALITY
  3 | 13 | 3 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) ON (r+i)<100;
@@ -779,10 +779,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) ON (r+i)<100;
  3 | 13 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
@@ -799,10 +799,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) WITH ORDINALITY
  3 | 13 | 3 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( foo_sql(11,13), foo_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100;
@@ -876,10 +876,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) WITH O
 (9 rows)
 
 --invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL)
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13);
@@ -893,10 +893,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13) WITH ORDINALITY AS f(i,s,o);
@@ -910,10 +910,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 1
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r);
@@ -927,10 +927,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r) WITH ORDINALITY AS f(i,s,o);
@@ -944,10 +944,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 3
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2);
@@ -965,10 +965,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2);
  16 | 20 | 20 | 10
 (10 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2) WITH ORDINALITY AS f(i,s,o);
@@ -986,10 +986,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2) WITH ORD
  16 | 20 | 20 | 10 | 5
 (10 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13);
@@ -1003,10 +1003,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13) WITH ORDINALITY AS f(i,s,o);
@@ -1020,10 +1020,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 1
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r);
@@ -1037,10 +1037,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r) WITH ORDINALITY AS f(i,s,o);
@@ -1054,10 +1054,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 3
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2);
@@ -1075,10 +1075,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2);
  16 | 20 | 20 | 10
 (10 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2) WITH ORDINALITY AS f(i,s,o);
@@ -1097,10 +1097,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2) WITH ORD
 (10 rows)
 
 -- selective rescan of multiple functions:
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_mat(10+r,13) );
@@ -1114,10 +1114,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_mat(10+r
  3 | 11 | 1 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(11,11) );
@@ -1131,10 +1131,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(11
  3 | 13 | 6 | 11 | 1
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(10+r,13) );
@@ -1148,10 +1148,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(10
  3 | 13 | 6 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( foo_sql(10+r1,13), foo_mat(10+r2,13) );
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index c7be273..d4e250f 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..ea45a2d 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_restore_state('sequence_test'::text, '99, false');
+ pg_sequence_restore_state 
+---------------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_restore_state('sequence_test'::regclass, '99, false');
+ pg_sequence_restore_state 
+---------------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | last_value | log_cnt | is_called 
+---------------+-------------+--------------+---------------------+-----------+-------------+-----------+------------+---------+-----------
+ foo_seq       |           1 |            1 | 9223372036854775807 |         1 |           1 | f         |          1 |       0 | f
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | last_value | log_cnt | is_called 
+---------------+-------------+--------------+---------------------+-----------+-------------+-----------+------------+---------+-----------
+ foo_seq       |           1 |            1 | 9223372036854775807 |         1 |           1 | f         |          2 |      31 | t
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 80c5706..d1bc3d7 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -100,14 +100,14 @@ SELECT table_name, column_name, is_updatable
  ro_view17  | b             | NO
  ro_view18  | a             | NO
  ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
  ro_view19  | start_value   | NO
  ro_view19  | increment_by  | NO
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
+ ro_view19  | last_value    | NO
+ ro_view19  | log_cnt       | NO
  ro_view19  | is_called     | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 470571b..b97670f 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -242,16 +242,16 @@ CREATE FUNCTION foo_mat(int,int) RETURNS setof foo_rescan_t AS 'begin for i in $
 -- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function
 -- is on the inner path of a nestloop join
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) ON (r+i)<100;
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) ON (r+i)<100;
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( foo_sql(11,13), foo_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100;
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN generate_series(11,13) f(i) ON (r+i)<100;
@@ -262,42 +262,42 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) WITH O
 
 --invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2) WITH ORDINALITY AS f(i,s,o);
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2) WITH ORDINALITY AS f(i,s,o);
 
 -- selective rescan of multiple functions:
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_mat(10+r,13) );
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(11,11) );
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(10+r,13) );
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( foo_sql(10+r1,13), foo_mat(10+r2,13) );
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) f(i);
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..aa60bfd 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_restore_state('sequence_test'::text, '99, false');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_restore_state('sequence_test'::regclass, '99, false');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
#55Petr Jelinek
petr@2ndquadrant.com
In reply to: Petr Jelinek (#54)
3 attachment(s)
Re: Sequence Access Method WIP

On 15/12/14 11:36, Petr Jelinek wrote:

On 10/12/14 03:33, Petr Jelinek wrote:

On 24/11/14 12:16, Heikki Linnakangas wrote:

About the rough edges:
- The AlterSequence is not prettiest code around as we now have to
create new relation when sequence AM is changed and I don't know how to
do that nicely
- I am not sure if I did the locking, command order and dependency
handling in AlterSequence correcly

This version does AlterSequence differently and better. Didn't attach
the gapless sequence again as that one is unchanged.

And another version, separated into patch-set of 3 patches.
First patch contains the seqam patch itself, not many changes there,
mainly docs/comments related. What I wrote in the previous email for
version 3 still applies.

Second patch adds DDL support. I originally wanted to make it
CREATE/DROP SEQUENCE ACCESS METHOD... but that would mean making ACCESS
a reserver keyword so I went for CREATE ACCESS METHOD FOR SEQUENCES
which does not need to change anything (besides adding METHOD to
unreserved keywords).
The DDL support uses the DefineStmt infra with some very small change as
the sequence ams are not schema qualified, but I think it's acceptable
and saves considerable amount of boilerplate.

And third patch is gapless sequence implementation updated to work with
the new DDL support with some tests added for checking if dependencies
work correctly. It also acts as example on how to make custom AMs.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-seqam-v5.patchtext/x-diff; name=0001-seqam-v5.patchDownload
>From 46a91f4f72f0f9409c516b89ca6f9f9fdf7e724c Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Mon, 22 Sep 2014 19:22:40 +0200
Subject: [PATCH 1/3] seqam v5

---
 contrib/postgres_fdw/expected/postgres_fdw.out |    4 +-
 contrib/postgres_fdw/sql/postgres_fdw.sql      |    2 +-
 doc/src/sgml/config.sgml                       |   23 +
 doc/src/sgml/ref/create_sequence.sgml          |   11 +
 src/backend/access/Makefile                    |    3 +-
 src/backend/access/common/reloptions.c         |   29 +-
 src/backend/access/sequence/Makefile           |   17 +
 src/backend/access/sequence/seqam.c            |  425 ++++++++
 src/backend/access/sequence/seqlocal.c         |  405 ++++++++
 src/backend/catalog/Makefile                   |    2 +-
 src/backend/catalog/objectaddress.c            |    1 +
 src/backend/commands/sequence.c                | 1314 +++++++++++++-----------
 src/backend/commands/tablecmds.c               |   29 +
 src/backend/nodes/copyfuncs.c                  |    4 +
 src/backend/nodes/equalfuncs.c                 |    4 +
 src/backend/parser/gram.y                      |   78 +-
 src/backend/parser/parse_utilcmd.c             |    1 +
 src/backend/utils/cache/catcache.c             |    6 +-
 src/backend/utils/cache/relcache.c             |   62 +-
 src/backend/utils/cache/syscache.c             |   23 +
 src/backend/utils/misc/guc.c                   |   12 +
 src/bin/pg_dump/pg_dump.c                      |   73 +-
 src/bin/psql/describe.c                        |   95 +-
 src/include/access/reloptions.h                |    5 +-
 src/include/access/seqam.h                     |   75 ++
 src/include/catalog/indexing.h                 |    5 +
 src/include/catalog/pg_proc.h                  |   22 +-
 src/include/catalog/pg_seqam.h                 |   78 ++
 src/include/commands/sequence.h                |   24 +-
 src/include/nodes/parsenodes.h                 |   10 +-
 src/include/utils/guc.h                        |    1 +
 src/include/utils/rel.h                        |   25 +-
 src/include/utils/syscache.h                   |    2 +
 src/test/regress/expected/rangefuncs.out       |  168 +--
 src/test/regress/expected/sanity_check.out     |    1 +
 src/test/regress/expected/sequence.out         |   28 +-
 src/test/regress/expected/updatable_views.out  |    4 +-
 src/test/regress/sql/rangefuncs.sql            |   42 +-
 src/test/regress/sql/sequence.sql              |    4 +-
 39 files changed, 2307 insertions(+), 810 deletions(-)
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqam.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqam.h
 create mode 100644 src/include/catalog/pg_seqam.h

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 583cce7..a331a29 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -2679,10 +2679,10 @@ ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2negative;
 create table loc1 (f1 serial, f2 text);
 create foreign table rem1 (f1 serial, f2 text)
   server loopback options(table_name 'loc1');
-select pg_catalog.setval('rem1_f1_seq', 10, false);
+select pg_catalog.setval('rem1_f1_seq', 9);
  setval 
 --------
-     10
+      9
 (1 row)
 
 insert into loc1(f2) values('hi');
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 83e8fa7..33041fb 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -441,7 +441,7 @@ ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2negative;
 create table loc1 (f1 serial, f2 text);
 create foreign table rem1 (f1 serial, f2 text)
   server loopback options(table_name 'loc1');
-select pg_catalog.setval('rem1_f1_seq', 10, false);
+select pg_catalog.setval('rem1_f1_seq', 9);
 insert into loc1(f2) values('hi');
 insert into rem1(f2) values('hi remote');
 insert into loc1(f2) values('bye');
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6bcb106..843bedf 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5607,6 +5607,29 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-default-sequenceam" xreflabel="default_sequenceam">
+      <term><varname>default_sequenceam</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>default_sequenceam</> configuration parameter</primary>
+      </indexterm>
+      <indexterm><primary>sequence access method</><secondary>default</></>
+      </term>
+      <listitem>
+       <para>
+        This variable specifies the default sequence access method to be used
+        for <type>SERIAL</> type and when a <command>CREATE SEQUENCE</> command
+        is executed without <command>USING</> clause.
+       </para>
+
+       <para>
+        An empty string selects the 'local' sequence am. This is also the
+        default.
+        If the value does not match the name of any existing sequence
+        access method, an error will be raised.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-check-function-bodies" xreflabel="check_function_bodies">
       <term><varname>check_function_bodies</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..e84ad32 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,7 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">sequence_access_method</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +224,16 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">sequence_access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </refsect1>
 
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..818da15 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+			  transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index f008fab..9a3e9c9 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -321,6 +321,7 @@ static bool need_initialization = true;
 static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 					int text_len, bool validate);
+static bytea *common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate);
 
 /*
  * initialize_reloptions
@@ -821,7 +822,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -854,6 +856,9 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 		case RELKIND_INDEX:
 			options = index_reloptions(amoptions, datum, false);
 			break;
+		case RELKIND_SEQUENCE:
+			options = sequence_reloptions(amoptions, datum, false);
+			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
 			break;
@@ -1299,13 +1304,31 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 /*
  * Parse options for indexes.
+ */
+bytea *
+index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for sequences.
+ */
+bytea *
+sequence_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+	return common_am_reloptions(amoptions, reloptions, validate);
+}
+
+/*
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
-bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+static bytea *
+common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..053da0a
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,425 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAm. That is also the basic default.  Local
+ * SeqAm assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAm mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAm would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAm allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module.  The on-disk format of
+ * Sequences has common part for all AMs, but each AM can specify extra columns
+ * used for storage of its private data. The storage itself is abstracted from
+ * AM and done by backend code.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAm data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*default_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_aminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Manager API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_extra_columns - get list of extra columns needed by the am
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_seqparams		- process the standard sequence parameters
+ *		seqam_dump_state	- dump sequence state (for pg_dump)
+ *		seqam_restore_state - restore sequence state (for pg_dump)
+ *
+ *		sequence_reloptions	- process reloptions - located in reloptions.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_extra_columns - get custom column list
+ */
+List *
+seqam_extra_columns(Oid seqamid)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple = NULL;
+	Form_pg_seqam seqamForm;
+	FunctionCallInfoData fcinfo;
+	Datum		ret;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	if (!RegProcedureIsValid(seqamForm->seqamextracols))
+		return NIL;
+
+	fmgr_info(seqamForm->seqamextracols, &procedure);
+
+	/*
+	 * have the seqam's proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 0, InvalidOid, NULL, NULL);
+
+	ret = FunctionCallInvoke(&fcinfo);
+
+	ReleaseSysCache(tuple);
+
+	return (List *)DatumGetPointer(ret);
+}
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+extern void
+seqam_init(Oid seqamid, List *seqparams,
+		   List *reloptions, bool is_init,
+		   Datum *values, bool *nulls)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple = NULL;
+	Form_pg_seqam seqamForm;
+	FunctionCallInfoData fcinfo;
+	char	   *validnsps[] = {NULL, NULL};
+	Datum       reloptions_transformed;
+	bytea	   *reloptions_parsed;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	fmgr_info(seqamForm->seqaminit, &procedure);
+
+	/* Allow am specific options */
+	validnsps[0] = NameStr(seqamForm->seqamname);
+
+	/*
+	 *  Parse AM-specific options, convert to text array form,
+	 *  retrieve the AM-option function and then validate.
+	 */
+	reloptions_transformed = transformRelOptions((Datum) NULL, reloptions,
+												 NULL, validnsps, false,
+												 false);
+
+	reloptions_parsed = sequence_reloptions(seqamForm->seqamreloptions,
+											reloptions_transformed, true);
+
+	/*
+	 * Have the seqam's proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqparams);
+	fcinfo.arg[1] = PointerGetDatum(reloptions_parsed);
+	fcinfo.arg[2] = BoolGetDatum(is_init);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.arg[4] = PointerGetDatum(nulls);
+	fcinfo.argnull[0] = seqparams == NULL;
+	fcinfo.argnull[1] = reloptions_parsed == NULL;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[3] = false;
+
+	FunctionCallInvoke(&fcinfo);
+
+	ReleaseSysCache(tuple);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(nrequested);
+	fcinfo.arg[3] = PointerGetDatum(last);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 3, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(new_value);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * seqam_dump_state - pg_dump support
+ */
+char *
+seqam_dump_state(Relation seqrel, SequenceHandle *seqh)
+{
+	FmgrInfo	procedure;
+	Datum		ret;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamdump, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 2, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+
+	return DatumGetCString(ret);
+}
+
+/*
+ * seqam_restore_state - restore from pg_dump
+ */
+void
+seqam_restore_state(Relation seqrel, SequenceHandle *seqh, char *state)
+{
+	FmgrInfo	procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamrestore, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = CStringGetDatum(state);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new default_sequenceam */
+bool
+check_default_seqam(char **newval, void **extra, GucSource source)
+{
+	if (**newval == '\0')
+		return true;
+
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.	Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence access method \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence access method \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+/*
+ * GetDefaultSeqAM -- get the OID of the current default sequence AM
+ *
+ * This exists to hide (and possibly optimize the use of) the
+ * default_seqam GUC variable.
+ */
+Oid
+GetDefaultSeqAM(void)
+{
+	/* Fast path for default_tablespace == "" */
+	if (default_seqam == NULL || default_seqam[0] == '\0')
+		return LOCAL_SEQAM_OID;
+
+	return get_seqam_oid(default_seqam, false);
+}
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("squence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..9cc5f11
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,405 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct FormLocalSequence
+{
+	FormData_pg_sequence seq;
+	int64		last_value;
+	int64		log_cnt;
+	bool		is_called;
+} FormLocalSequence;
+
+SeqAMColumnData seqam_local_cols[] = {
+	{"last_value", INT8OID, -1, true},
+	{"log_cnt", INT8OID, -1, true},
+	{"is_called", BOOLOID, -1, true}
+};
+#define SEQAM_LOCAL_EXTRACOLS_CNT 3
+#define SEQAM_LOCAL_COL_LASTVAL SEQ_COL_LASTCOL + 1
+#define SEQAM_LOCAL_COL_LOGCNT SEQ_COL_LASTCOL + 2
+#define SEQAM_LOCAL_COL_CALLED SEQ_COL_LASTCOL + 3
+
+/*
+ * seqam_local_extracols()
+ *
+ * Get definitions for extra columns needed by a local sequence
+ */
+Datum
+seqam_local_extracols(PG_FUNCTION_ARGS)
+{
+	List *ret = NIL;
+	int i;
+
+	for (i = 0; i < SEQAM_LOCAL_EXTRACOLS_CNT; i++)
+		ret = lappend(ret, &seqam_local_cols[i]);
+
+	PG_RETURN_POINTER(ret);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum       reloptions = PG_GETARG_DATUM(0);
+	bool        validate = PG_GETARG_BOOL(1);
+	bytea      *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	List *params = (List *)(PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));
+	bool is_init = PG_GETARG_BOOL(2);
+	Datum *values = (Datum *)PG_GETARG_POINTER(3);
+	bool *nulls = (bool *)PG_GETARG_POINTER(4);
+
+	DefElem    *restart_value = NULL;
+	int64		last_value,
+				min_value,
+				max_value;
+	ListCell   *param;
+
+	foreach(param, params)
+	{
+		DefElem    *defel = (DefElem *) lfirst(param);
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			/* The redundacy check is done in the main sequence code. */
+			restart_value = defel;
+			break;
+		}
+	}
+
+	/* RESTART [WITH] */
+	if (restart_value != NULL)
+	{
+		Datum		last_value;
+
+		if (restart_value->arg != NULL)
+			last_value = defGetInt64(restart_value);
+		else
+			last_value = values[SEQ_COL_STARTVAL - 1];
+
+		values[SEQAM_LOCAL_COL_LASTVAL - 1] = last_value;
+		values[SEQAM_LOCAL_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+	else if (is_init)
+	{
+		values[SEQAM_LOCAL_COL_LASTVAL - 1] = values[SEQ_COL_STARTVAL - 1];
+		values[SEQAM_LOCAL_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+
+	last_value = DatumGetInt64(values[SEQAM_LOCAL_COL_LASTVAL - 1]);
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+
+	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
+	if (last_value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, last_value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
+					  bufs, bufm)));
+	}
+	if (last_value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, last_value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
+				   bufs, bufm)));
+	}
+
+	/* log_cnt */
+	values[SEQAM_LOCAL_COL_LOGCNT - 1] = Int64GetDatum(0);
+
+	nulls[SEQAM_LOCAL_COL_LASTVAL - 1] = false;
+	nulls[SEQAM_LOCAL_COL_LOGCNT - 1] = false;
+	nulls[SEQAM_LOCAL_COL_CALLED - 1] = false;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormLocalSequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				logit = false;
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	next = result = seq->last_value;
+	incby = seq->seq.increment_by;
+	maxv = seq->seq.max_value;
+	minv = seq->seq.min_value;
+	is_cycled = seq->seq.is_cycled;
+	fetch = nrequested;
+	log = seq->log_cnt;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!seq->is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !seq->is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (seq->is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	START_CRIT_SECTION();
+
+	/* Log our cached data. */
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		seq->log_cnt = 0;
+
+		sequence_save_tuple(seqh, NULL, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	seq->log_cnt = log;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, false);
+
+	END_CRIT_SECTION();
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    next = PG_GETARG_INT64(2);
+	FormLocalSequence *seq;
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	seq->log_cnt = 0;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_dump()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ *
+ * Format is: '<last_value::bigint>, <is_called::boolean>'
+ */
+Datum
+seqam_local_dump(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	FormLocalSequence  *seq;
+	char		buf[19 + 1 + 1 + 1 + 1]; /* int64 + colon + space + t/f + null byte */
+	char	   *result;
+
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	pg_lltoa(seq->last_value, buf);
+	strcat(buf, seq->is_called ? ", t" : ", f");
+	result = pstrdup(buf);
+
+	PG_RETURN_CSTRING(result);
+}
+
+/*
+ * seqam_local_restore()
+ *
+ * Restore previously dumpred state of local sequence (used by pg_dump)
+ *
+ * Format is: '<last_value::bigint>, <is_called::boolean>'
+*/
+Datum
+seqam_local_restore(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			   *state = PG_GETARG_CSTRING(2);
+	FormLocalSequence  *seq;
+	int64				last_value;
+	bool				is_called;
+	char			   *buf;
+	char			   *ptr = state;
+	int					len;
+
+	/* Check that we can find comma. */
+	while (*ptr != ',' && *ptr != '\0')
+		ptr++;
+
+	if (*ptr == '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for local sequence state: \"%s\"",
+						state)));
+
+	/*
+	 * Copy the part of the string before comma into work buffer
+	 * for scanint8, it will handle leading/trailing whitespace correctly.
+	 */
+	len = ptr - state;
+	buf = palloc(len + 1);
+	memcpy(buf, state, len);
+	buf[len] = '\0';
+	scanint8(buf, false, &last_value);
+
+	/* Skip leading/trailing whitespace for parse_bool_with_len. */
+	ptr++;
+	while (isspace((unsigned char) *ptr))
+		ptr++;
+
+	len = strlen(ptr);
+	while (len > 0 && isspace((unsigned char) ptr[len - 1]))
+		len--;
+
+	/* Parse out the boolean */
+	if (!parse_bool_with_len(ptr, len, &is_called))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for local sequence state: \"%s\"",
+						state)));
+
+	pfree(buf);
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->last_value = last_value;
+	seq->is_called = is_called;
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a403c64..147c571 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 825d8b2..2412a24 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 622ccf7..5834b2d 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,15 +14,21 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -35,19 +41,13 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -80,6 +80,14 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTuple	tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -90,13 +98,124 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static Oid get_new_seqam_oid(Oid oldAM, char *accessMethod);
+
+
+/*
+ * Build template column definition for a sequence relation.
+ */
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_from_type = false;
+	coldef->storage = 0;
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Create the relation for sequence.
+ */
+static Oid
+DefineSequenceRelation(RangeVar *rel, List *cols,
+					   bool if_not_exists, Oid ownerId)
+{
+	CreateStmt *stmt = makeNode(CreateStmt);
+
+	stmt->relation = rel;
+	stmt->tableElts = cols;
+	stmt->inhRelations = NIL;
+	stmt->constraints = NIL;
+	stmt->options = NIL;
+	stmt->oncommit = ONCOMMIT_NOOP;
+	stmt->tablespacename = NULL;
+	stmt->if_not_exists = if_not_exists;
+
+	return DefineRelation(stmt, RELKIND_SEQUENCE, ownerId);
+}
+
+/*
+ * Add additional sequence am columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(List *amcols)
+{
+	List	   *seqcols;
+	ListCell   *amcol;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		coldef->is_not_null = true;
+
+		switch (colid)
+		{
+			case SEQ_COL_NAME:
+				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
+				coldef->colname = "sequence_name";
+				break;
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+		}
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	foreach(amcol, amcols)
+	{
+		SeqAMColumn	amcolumn = lfirst(amcol);
+
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		coldef->typeName = makeTypeNameFromOid(amcolumn->coltyp,
+											   amcolumn->coltypmod);
+		coldef->colname = amcolumn->colname;
+		coldef->is_not_null = amcolumn->colnotnull;
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	return seqcols;
+}
 
 
 /*
@@ -108,15 +227,17 @@ DefineSequence(CreateSeqStmt *seq)
 {
 	FormData_pg_sequence new;
 	List	   *owned_by;
-	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	Relation	rel;
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
+	Datum	   *value;
+	bool	   *null;
+	int			colid;
 	NameData	name;
+	List	   *seqcols,
+			   *amcols;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -142,104 +263,75 @@ DefineSequence(CreateSeqStmt *seq)
 		}
 	}
 
-	/* Check and set all option values */
+	/* Check and set all param values */
 	init_params(seq->options, true, &new, &owned_by);
 
+	/* Build column definitions. */
+	seqamid = get_new_seqam_oid(InvalidOid, seq->accessMethod);
+	amcols = seqam_extra_columns(seqamid);
+
+	seqcols = BuildSeqColumnDefList(amcols);
+
+	value = palloc0(list_length(seqcols) * sizeof(Datum));
+	null = palloc0(list_length(seqcols) * sizeof(bool));
+
 	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
+	 * Fill value[] and null[] for the tuple.
 	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
 	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
+		null[colid - 1] = false;
 
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
+		switch (colid)
 		{
 			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
 				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
+				value[colid - 1] = NameGetDatum(&name);
 				break;
 			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
+				value[colid - 1] = Int64GetDatumFast(new.start_value);
 				break;
 			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
+				value[colid - 1] = Int64GetDatumFast(new.increment_by);
 				break;
 			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
+				value[colid - 1] = Int64GetDatumFast(new.max_value);
 				break;
 			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
+				value[colid - 1] = Int64GetDatumFast(new.min_value);
 				break;
 			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
+				value[colid - 1] = Int64GetDatumFast(new.cache_value);
 				break;
 			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
+				value[colid - 1] = BoolGetDatum(new.is_cycled);
 				break;
 		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
 
-	stmt->relation = seq->sequence;
-	stmt->inhRelations = NIL;
-	stmt->constraints = NIL;
-	stmt->options = NIL;
-	stmt->oncommit = ONCOMMIT_NOOP;
-	stmt->tablespacename = NULL;
-	stmt->if_not_exists = seq->if_not_exists;
+	/* Let AM fill the value[] and null[] for the tuple as well. */
+	seqam_init(seqamid, seq->options, seq->amoptions, true,
+			   value, null);
 
-	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
+	/* Create the sequence relation */
+	seqoid = DefineSequenceRelation(seq->sequence, seqcols,
+									seq->if_not_exists, seq->ownerId);
 	Assert(seqoid != InvalidOid);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
 	/* now initialize the sequence's data */
+	tupDesc = RelationGetDescr(rel);
 	tuple = heap_form_tuple(tupDesc, value, null);
+
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -266,56 +358,68 @@ DefineSequence(CreateSeqStmt *seq)
 void
 ResetSequence(Oid seq_relid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	HeapTuple	newtup;
+	Relation	seqrel;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle  *seqh;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	seqh = sequence_open(seq_relid);
+	tuple = sequence_read_tuple(seqh);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
-	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	seqrel = seqh->rel;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	memset(replaces, true, tupDesc->natts * sizeof(bool));
+
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+
+	seqam_init(seqrel->rd_rel->relam,
+			   list_make1(makeDefElem("restart", NULL)), NULL, false,
+			   values, nulls);
+
+	newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqh->rel, seqh->rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqh->rel, newtup);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh->elm->cached = seqh->elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(seqh);
 }
 
 /*
@@ -368,23 +472,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -400,16 +488,23 @@ Oid
 AlterSequence(AlterSeqStmt *stmt)
 {
 	Oid			relid;
+	Oid			oldamid;
+	Oid			seqamid;
 	SeqTable	elm;
+	HeapTuple	tuple;
+	HeapTuple	newtup;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle *seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
+	relid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
 	if (relid == InvalidOid)
 	{
 		ereport(NOTICE,
@@ -418,7 +513,9 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidOid;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
+	elm = seqh->elm;
+	seqrel = seqh->rel;
 
 	/* allow ALTER to sequence owner only */
 	if (!pg_class_ownercheck(relid, GetUserId()))
@@ -426,48 +523,107 @@ AlterSequence(AlterSeqStmt *stmt)
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
-
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
 
-	MarkBufferDirty(buf);
+	oldamid = seqrel->rd_rel->relam;
+	seqamid = get_new_seqam_oid(seqrel->rd_rel->relam, stmt->accessMethod);
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
+	/*
+	 * If we are changing sequence AM, we need to alter
+	 * the sequence relation.
+	 */
+	if (seqamid != oldamid)
 	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		List   *oldamcols = seqam_extra_columns(oldamid);
+		List   *newamcols = seqam_extra_columns(seqamid);
+		ListCell *amcol;
+		List   *atcmds = NIL;
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		sequence_release_tuple(seqh);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		/*
+		 * Create a new storage file for the sequence.
+		 * See ResetSequence for more info.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		/* Build DROP commands for columns of old AM. */
+		foreach(amcol, oldamcols)
+		{
+			SeqAMColumn	amcolumn = lfirst(amcol);
+			AlterTableCmd *cmd = makeNode(AlterTableCmd);
+
+			cmd->subtype = AT_DropColumnFromSequence;
+			cmd->name = amcolumn->colname;
+			cmd->behavior = DROP_RESTRICT;
+			cmd->missing_ok = TRUE;
+			atcmds = lappend(atcmds, cmd);
+		}
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* Build ADD commands for columns of new AM. */
+		foreach(amcol, newamcols)
+		{
+			SeqAMColumn	amcolumn = lfirst(amcol);
+			ColumnDef  *coldef = makeSeqColumnDef();
+			AlterTableCmd *cmd = makeNode(AlterTableCmd);
+
+			coldef->typeName = makeTypeNameFromOid(amcolumn->coltyp,
+												   amcolumn->coltypmod);
+			coldef->colname = amcolumn->colname;
+			coldef->is_not_null = amcolumn->colnotnull;
+
+			cmd->subtype = AT_AddColumnToSequence;
+			cmd->def = (Node *) coldef;
+			atcmds = lappend(atcmds, cmd);
+		}
 
-		PageSetLSN(page, recptr);
-	}
+		/* Run the commands to add/remove columns. */
+		AlterTableInternal(relid, atcmds, false);
 
-	END_CRIT_SECTION();
+		tupDesc = RelationGetDescr(seqrel);
 
-	UnlockReleaseBuffer(buf);
+		values = (Datum *) repalloc(values, tupDesc->natts * sizeof(Datum));
+		nulls = (bool *) repalloc(nulls, tupDesc->natts * sizeof(bool));
+
+		/* Let the new sequence AM initialize. */
+		seqam_init(seqamid, stmt->options, stmt->amoptions, seqamid != oldamid,
+				   values, nulls);
+
+		/* Rebuild the sequence tuple and save it. */
+		newtup = heap_form_tuple(tupDesc, values, nulls);
+		fill_seq_with_data(seqh->rel, newtup);
+
+		/* Update the catalog. */
+		seqrel_update_relam(relid, seqamid);
+	}
+	else
+	{
+		/* Let sequence AM update the tuple. */
+		replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+		memset(replaces, true, tupDesc->natts * sizeof(bool));
+		seqam_init(seqamid, stmt->options, stmt->amoptions, seqamid != oldamid,
+				   values, nulls);
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
+		sequence_save_tuple(seqh, newtup, true);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	elm->cached = elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
@@ -475,7 +631,7 @@ AlterSequence(AlterSeqStmt *stmt)
 
 	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(seqh);
 
 	return relid;
 }
@@ -516,29 +672,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle *seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
+	elm = seqh->elm;
+	seqrel = seqh->rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -556,121 +707,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
 
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -679,261 +724,119 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_close(seqh);
 
 	return result;
 }
 
+
 Datum
 currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle *seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh->elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
-
-	if (!elm->last_valid)
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
-
-	result = elm->last;
-
-	relation_close(seqrel, NoLock);
-
-	PG_RETURN_INT64(result);
-}
-
-Datum
-lastval(PG_FUNCTION_ARGS)
-{
-	Relation	seqrel;
-	int64		result;
-
-	if (last_used_seq == NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("lastval is not yet defined in this session")));
-
-	/* Someone may have dropped the sequence since the last nextval() */
-	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("lastval is not yet defined in this session")));
-
-	seqrel = open_share_lock(last_used_seq);
-
-	/* nextval() must have already been called for this sequence */
-	Assert(last_used_seq->last_valid);
-
-	if (pg_class_aclcheck(last_used_seq->relid, GetUserId(),
-						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
-
-	result = last_used_seq->last;
-	relation_close(seqrel, NoLock);
-
-	PG_RETURN_INT64(result);
-}
-
-/*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
- */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
-{
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-
-	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
-
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh->rel))));
 
-	/* read-only transactions may only modify temp sequences */
-	if (!seqrel->rd_islocaltemp)
-		PreventCommandIfReadOnly("setval()");
-
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
+	if (!seqh->elm->last_valid)
 		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
-
-	/* In any case, forget any future cached numbers */
-	elm->cached = elm->last;
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
+						RelationGetRelationName(seqh->rel))));
 
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
+	result = seqh->elm->last;
 
-	MarkBufferDirty(buf);
+	sequence_close(seqh);
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+	PG_RETURN_INT64(result);
+}
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+Datum
+lastval(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel;
+	int64		result;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+	if (last_used_seq == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("lastval is not yet defined in this session")));
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+	/* Someone may have dropped the sequence since the last nextval() */
+	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("lastval is not yet defined in this session")));
 
-		PageSetLSN(page, recptr);
-	}
+	seqrel = open_share_lock(last_used_seq);
 
-	END_CRIT_SECTION();
+	/* nextval() must have already been called for this sequence */
+	Assert(last_used_seq->last_valid);
 
-	UnlockReleaseBuffer(buf);
+	if (pg_class_aclcheck(last_used_seq->relid, GetUserId(),
+						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+						RelationGetRelationName(seqrel))));
 
+	result = last_used_seq->last;
 	relation_close(seqrel, NoLock);
+
+	PG_RETURN_INT64(result);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the setval procedure.
  */
 Datum
 setval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle *seqh;
 
-	do_setval(relid, next, true);
+	/* open and AccessShareLock sequence */
+	seqh = sequence_open(relid);
+	elm = seqh->elm;
+	seqrel = seqh->rel;
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+						RelationGetRelationName(seqrel))));
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-	do_setval(relid, next, iscalled);
+	seqam_setval(seqrel, seqh, next);
+
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
+
+	sequence_close(seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -992,21 +895,21 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+SequenceHandle *
+sequence_open(Oid seq_relid)
 {
-	SeqTable	elm;
-	Relation	seqrel;
-	bool		found;
+	SequenceHandle *seqh;
+	SeqTable		elm;
+	Relation		seqrel;
+	bool			found;
 
 	/* Find or create a hash table entry for this sequence */
 	if (seqhashtab == NULL)
 		create_seq_hashtable();
 
-	elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);
+	elm = (SeqTable) hash_search(seqhashtab, &seq_relid, HASH_ENTER, &found);
 
 	/*
 	 * Initialize the new hash table entry if it did not exist already.
@@ -1047,45 +950,60 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 		elm->cached = elm->last;
 	}
 
+	seqh = (SequenceHandle *) palloc(sizeof(SequenceHandle));
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup = NULL;
+
+	return seqh;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	sequence_release_tuple(seqh);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
+	Form_pg_sequence seq_form;
+
+	if (HeapTupleIsValid(seqh->tup))
+		return seqh->tup;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	seqh->tup = palloc0(HEAPTUPLESIZE);
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of seqh->tup */
+	seqh->tup->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup->t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1095,33 +1013,116 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup->t_data) != InvalidTransactionId)
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		HeapTupleHeaderSetXmax(seqh->tup->t_data, InvalidTransactionId);
+		seqh->tup->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
 	}
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	seq_form = (Form_pg_sequence) GETSTRUCT(seqh->tup);
 
 	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	seqh->elm->increment = seq_form->increment_by;
+
+	return seqh->tup;
+}
+
+/*
+ * Update the page, optionally do WAL logging of the tuple
+ */
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal)
+{
+	Page	page;
+
+	Assert(HeapTupleIsValid(seqh->tup));
+
+	page = BufferGetPage(seqh->buf);
+
+	/* Only do tuple replacement if we got valid tuple on input. */
+	if (HeapTupleIsValid(newtup))
+	{
+		Page	temppage;
+
+		temppage = PageGetTempPageCopySpecial(page);
+
+		if (PageAddItem(temppage, (Item) newtup->t_data, newtup->t_len,
+						FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+			elog(PANIC, "sequence_save_tuple: failed to add item to page");
+
+		PageSetLSN(temppage, PageGetLSN(page));
+
+		START_CRIT_SECTION();
 
-	return seq;
+		PageRestoreTempPage(temppage, page);
+		seqh->tup = newtup;
+		MarkBufferDirty(seqh->buf);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, newtup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+	else
+	{
+		START_CRIT_SECTION();
+
+		MarkBufferDirty(seqh->buf);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, seqh->tup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+}
+
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (HeapTupleIsValid(seqh->tup))
+	{
+		pfree(seqh->tup);
+		seqh->tup = NULL;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
+	{
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
+	}
+}
+
+/*
+ * Returns true if sequence was not WAL logged since checkpoint
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr = GetRedoRecPtr();
+
+	Assert(BufferIsValid(seqh->buf));
+
+	page = BufferGetPage(seqh->buf);
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1131,13 +1132,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1208,13 +1209,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1223,7 +1217,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1233,7 +1226,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1242,7 +1234,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1250,14 +1241,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1265,7 +1254,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1319,48 +1307,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1375,14 +1321,13 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1398,8 +1343,7 @@ process_owned_by(Relation seqrel, List *owned_by)
 
 	nnames = list_length(owned_by);
 	Assert(nnames > 0);
-	if (nnames == 1)
-	{
+	if (nnames == 1)	{
 		/* Must be OWNED BY NONE */
 		if (strcmp(strVal(linitial(owned_by)), "none") != 0)
 			ereport(ERROR,
@@ -1486,20 +1430,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  *seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	seqh = sequence_open(relid);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh->rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1517,7 +1458,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1525,12 +1466,99 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_close(seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_dump_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *result;
+	SequenceHandle  *seqh;
+
+	seqh = sequence_open(relid);
+
+	result = seqam_dump_state(seqh->rel, seqh);
+
+	sequence_close(seqh);
+
+	PG_RETURN_CSTRING(result);
+}
+
+Datum
+pg_sequence_restore_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *state = PG_GETARG_CSTRING(1);
+	SequenceHandle  *seqh;
+
+	seqh = sequence_open(relid);
+
+	seqam_restore_state(seqh->rel, seqh, state);
+
+	sequence_close(seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+		CatalogUpdateIndexes(rd, ctup);
+	}
+
+	heap_freetuple(ctup);
+	heap_close(rd, RowExclusiveLock);
+	CommandCounterIncrement();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1582,6 +1610,7 @@ seq_redo(XLogReaderState *record)
 	pfree(localpage);
 }
 
+
 /*
  * Flush cached sequence information.
  */
@@ -1596,3 +1625,92 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+static Oid
+get_new_seqam_oid(Oid oldAM, char *accessMethod)
+{
+
+	if (oldAM && accessMethod == NULL)
+		return oldAM;
+	else if (accessMethod == NULL || strcmp(accessMethod, DEFAULT_SEQAM) == 0)
+		return GetDefaultSeqAM();
+	else
+		return get_seqam_oid(accessMethod, false);
+}
+
+
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64 next = *value;
+	int64 rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 66d5083..9249558 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -265,6 +265,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -2834,6 +2835,8 @@ AlterTableGetLockLevel(List *cmds)
 				 */
 			case AT_DropColumn:	/* change visible to SELECT */
 			case AT_AddColumnToView:	/* CREATE VIEW */
+			case AT_AddColumnToSequence:	/* ALTER SEQUENCE USING */
+			case AT_DropColumnFromSequence:	/* ALTER SEQUENCE USING */
 			case AT_DropOids:	/* calls AT_DropColumn */
 			case AT_EnableAlwaysRule:	/* may change SELECT rules */
 			case AT_EnableReplicaRule:	/* may change SELECT rules */
@@ -3093,6 +3096,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			/* Recursion occurs during execution phase */
 			pass = AT_PASS_ADD_COL;
 			break;
+		case AT_AddColumnToSequence:	/* add column via ALTER SEQUENCE
+										   USING */
+			ATSimplePermissions(rel, ATT_SEQUENCE);
+			ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
+			/* This command never recurses */
+			pass = AT_PASS_MISC;
+			break;
 		case AT_ColumnDefault:	/* ALTER COLUMN DEFAULT */
 
 			/*
@@ -3143,6 +3153,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			/* Recursion occurs during execution phase */
 			pass = AT_PASS_DROP;
 			break;
+		case AT_DropColumnFromSequence:	/* drop column via ALTER SEQUENCE
+										   USING */
+			ATSimplePermissions(rel, ATT_SEQUENCE);
+			ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
+			/* This command never recurses */
+			pass = AT_PASS_MISC;
+			break;
 		case AT_AddIndex:		/* ADD INDEX */
 			ATSimplePermissions(rel, ATT_TABLE);
 			/* This command never recurses */
@@ -3395,6 +3412,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		case AT_AddColumn:		/* ADD COLUMN */
 		case AT_AddColumnToView:		/* add column via CREATE OR REPLACE
 										 * VIEW */
+		case AT_AddColumnToSequence:	/* add column via ALTER SEQUENCE
+										   USING */
 			ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
 							false, false, false, lockmode);
 			break;
@@ -3424,6 +3443,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
 			break;
 		case AT_DropColumn:		/* DROP COLUMN */
+		case AT_DropColumnFromSequence:		/* ALTER SEQUENCE USING */
 			ATExecDropColumn(wqueue, rel, cmd->name,
 					 cmd->behavior, false, false, cmd->missing_ok, lockmode);
 			break;
@@ -4207,6 +4227,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4271,6 +4294,9 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a foreign table");
 			break;
+		case ATT_SEQUENCE:
+			msg = _("\"%s\" is not a sequence");
+			break;
 		default:
 			/* shouldn't get here, add all necessary cases above */
 			msg = _("\"%s\" is of the wrong type");
@@ -9042,6 +9068,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 		case RELKIND_INDEX:
 			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
+		case RELKIND_SEQUENCE:
+			(void) sequence_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f1a24f5..ffde815 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3362,7 +3362,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3375,7 +3377,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6e8b308..2d731d6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1568,7 +1568,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1579,7 +1581,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 679e1bb..976eb6b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3531,7 +3532,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = DEFAULT_SEQAM;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3553,7 +3580,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3561,11 +3612,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3624,7 +3698,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 3ccdbb7..64f75b3 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -399,6 +399,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 2e4d0b3..a648a30 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 24c92e7..a324552 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1055,11 +1057,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1534,6 +1539,39 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for an index relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	MemoryContext	indexcxt;
+	Form_pg_seqam	amform;
+
+	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+									 RelationGetRelationName(rel),
+									 ALLOCSET_SMALL_MINSIZE,
+									 ALLOCSET_SMALL_INITSIZE,
+									 ALLOCSET_SMALL_MAXSIZE);
+	rel->rd_indexcxt = indexcxt;
+
+	rel->rd_aminfo = (RelationAmInfo *)
+		MemoryContextAllocZero(rel->rd_indexcxt,
+							   sizeof(RelationAmInfo));
+
+	/*
+	 * Make a copy of the pg_am entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+}
 
 /*
  *		formrdesc
@@ -4806,6 +4844,22 @@ load_relcache_init_file(bool shared)
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
+		else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+		{
+			MemoryContext indexcxt;
+			Assert(!rel->rd_isnailed);
+			Assert(false);
+
+			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+											 RelationGetRelationName(rel),
+											 ALLOCSET_SMALL_MINSIZE,
+											 ALLOCSET_SMALL_INITSIZE,
+											 ALLOCSET_SMALL_MAXSIZE);
+			rel->rd_indexcxt = indexcxt;
+			/* set up zeroed fmgr-info vectors */
+			rel->rd_aminfo = (RelationAmInfo *)
+				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+		}
 		else
 		{
 			/* Count nailed rels to ensure we have 'em all */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index bd27168..4f83b10 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAmNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_amname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAmOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f6df077..7817a15 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2788,6 +2789,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"default_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence am for any new sequences."),
+			gettext_noop("An empty string selects the 'local' sequence am."),
+			GUC_IS_NAME | GUC_NOT_IN_SAMPLE
+		},
+		&default_seqam,
+		"",
+		check_default_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 981fdf1..8d49f69 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14584,7 +14584,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = "local";
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14654,15 +14655,41 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	}
 #endif
 
-	startv = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
+	startv = pg_strdup(PQgetvalue(res, 0, 1));
+	incby = pg_strdup(PQgetvalue(res, 0, 2));
 	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
+		maxv = pg_strdup(PQgetvalue(res, 0, 3));
 	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
+		minv = pg_strdup(PQgetvalue(res, 0, 4));
+	cache = pg_strdup(PQgetvalue(res, 0, 5));
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
+	PQclear(res);
+
+	res = ExecuteSqlQuery(fout, "SELECT EXISTS(SELECT 1 "
+								"FROM pg_catalog.pg_class c, "
+								"pg_catalog.pg_namespace n "
+								"WHERE n.oid = c.relnamespace "
+								"AND nspname = 'pg_catalog'"
+								"AND c.relname = 'pg_seqam' "
+								"AND c.relkind = 'r');",
+						  PGRES_TUPLES_OK);
+	if (strcmp(PQgetvalue(res, 0, 0), "t") == 0)
+	{
+		PQclear(res);
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		amname = pg_strdup(PQgetvalue(res, 0, 0));
+	}
+
+	PQclear(res);
+
 	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
@@ -14704,6 +14731,7 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14770,8 +14798,6 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
-	PQclear(res);
-
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(delqry);
 	destroyPQExpBuffer(labelq);
@@ -14786,17 +14812,30 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
+	char	   *state;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	res = ExecuteSqlQuery(fout, "SELECT EXISTS(SELECT 1 "
+								"FROM pg_catalog.pg_proc p, "
+								"pg_catalog.pg_namespace n "
+								"WHERE n.oid = p.pronamespace "
+								"AND nspname = 'pg_catalog'"
+								"AND p.proname = 'pg_sequence_dump_state');",
+						  PGRES_TUPLES_OK);
+
+	if (strcmp(PQgetvalue(res, 0, 0), "t") == 0)
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(last_value::text || ', ' || is_called::text) FROM %s",
+						  fmtId(tbinfo->dobj.name));
+	else
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_dump_state(%s))",
+						  fmtId(tbinfo->dobj.name));
 
+	PQclear(res);
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
 	if (PQntuples(res) != 1)
@@ -14808,14 +14847,12 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+	state = PQgetvalue(res, 0, 0);
 
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+	appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_restore_state(");
 	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+	appendPQExpBuffer(query, ", %s);\n", state);
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4cda07d..45a4614 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1395,30 +1395,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1462,6 +1438,14 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S' && !verbose)
+		appendPQExpBufferStr(&buf, " AND attname IN ('sequence_name', "
+							 "'start_value', 'increment_by', 'max_value', "
+							 "'min_value', 'cache_value', 'is_cycled')");
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
 	res = PSQLexec(buf.data);
@@ -1469,6 +1453,39 @@ describeOneTableDetails(const char *schemaname,
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1780,6 +1797,29 @@ describeOneTableDetails(const char *schemaname,
 		/* Footer information about a sequence */
 		PGresult   *result = NULL;
 
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
+		/*
+		 * If we get no rows back, don't show anything (obviously). We should
+		 * never get more than one row back, but if we do, just ignore it and
+		 * don't print anything.
+		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
+		PQclear(result);
+
 		/* Get the column that owns this sequence */
 		printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
 						  "\n   pg_catalog.quote_ident(relname) || '.' ||"
@@ -1797,6 +1837,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1806,11 +1848,6 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
-		/*
-		 * If we get no rows back, don't show anything (obviously). We should
-		 * never get more than one row back, but if we do, just ignore it and
-		 * don't print anything.
-		 */
 		PQclear(result);
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index e7b6bb5..d45ede6 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -46,8 +46,9 @@ typedef enum relopt_kind
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
 	RELOPT_KIND_BRIN = (1 << 10),
+	RELOPT_KIND_SEQUENCE = (1 << 11),
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -272,6 +273,8 @@ extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
 extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
 				 bool validate);
+extern bytea *sequence_reloptions(RegProcedure amoptions, Datum reloptions,
+				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..c4f6f73
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef struct SeqAMColumnData
+{
+	char   *colname;
+	Oid		coltyp;
+	int32	coltypmod;
+	bool	colnotnull;
+} SeqAMColumnData;
+
+typedef SeqAMColumnData *SeqAMColumn;
+
+extern char *default_seqam;
+
+extern Oid GetDefaultSeqAM(void);
+
+extern List *seqam_extra_columns(Oid seqamid);
+extern void seqam_init(Oid seqamid, List *seqparams,
+					   List *reloptions, bool is_init,
+					   Datum *values, bool *nulls);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern char *seqam_dump_state(Relation seqrel, SequenceHandle *seqh);
+extern void seqam_restore_state(Relation seqrel, SequenceHandle *seqh, char *state);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern SequenceHandle *sequence_open(Oid seq_relid);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+
+extern Datum seqam_local_extracols(PG_FUNCTION_ARGS);
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_dump(PG_FUNCTION_ARGS);
+extern Datum seqam_local_restore(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index a680229..1295c76 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAmNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAmOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9edfdb8..0b6e148 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1815,10 +1815,12 @@ DATA(insert OID = 1575 (  currval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 20
 DESCR("sequence current value");
 DATA(insert OID = 1576 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 20 "2205 20" _null_ _null_ _null_ _null_  setval_oid _null_ _null_ _null_ ));
 DESCR("set sequence value");
-DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ ));
-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_ ));
+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_ ));
+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_ ));
 DESCR("I/O");
@@ -5080,6 +5082,22 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6022 (  seqam_local_extracols	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ seqam_local_extracols _null_ _null_ _null_ ));
+DESCR("Get local SequenceAM extra columns");
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "2281 17 16 2281 2281" _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_dump	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 2275 "2281 2281" _null_ _null_ _null_ _null_ seqam_local_dump _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_restore	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 2275" _null_ _null_ _null_ _null_ seqam_local_restore _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..e08c935
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,78 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	32
+
+CATALOG(pg_seqam,32)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamextracols;		/* get extra columns this am stores in the sequence relation */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamdump;			/* dump state, used by pg_dump */
+	regproc		seqamrestore;		/* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						8
+#define Anum_pg_seqam_amname				1
+#define Anum_pg_seqam_amextracols			2
+#define Anum_pg_seqam_amreloptions			3
+#define Anum_pg_seqam_aminit				4
+#define Anum_pg_seqam_amalloc				5
+#define Anum_pg_seqam_amsetval				6
+#define Anum_pg_seqam_amdump				7
+#define Anum_pg_seqam_amrestore				8
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 2 (  local		seqam_local_extracols seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_dump seqam_local_restore));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 2
+
+#define DEFAULT_SEQAM	""
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 1baf43d..ff2563b 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -23,15 +23,12 @@
 typedef struct FormData_pg_sequence
 {
 	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,18 +38,14 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  */
 
 #define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
-
+#define SEQ_COL_STARTVAL		2
+#define SEQ_COL_INCBY			3
+#define SEQ_COL_MAXVALUE		4
+#define SEQ_COL_MINVALUE		5
+#define SEQ_COL_CACHE			6
+#define SEQ_COL_CYCLE			7
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_CYCLE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -67,10 +60,11 @@ extern Datum nextval(PG_FUNCTION_ARGS);
 extern Datum nextval_oid(PG_FUNCTION_ARGS);
 extern Datum currval_oid(PG_FUNCTION_ARGS);
 extern Datum setval_oid(PG_FUNCTION_ARGS);
-extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_dump_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_restore_state(PG_FUNCTION_ARGS);
 
 extern Oid	DefineSequence(CreateSeqStmt *stmt);
 extern Oid	AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b1dfa85..2dd2f71 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1285,6 +1285,7 @@ typedef enum AlterTableType
 	AT_AddColumn,				/* add column */
 	AT_AddColumnRecurse,		/* internal to commands/tablecmds.c */
 	AT_AddColumnToView,			/* implicitly via CREATE OR REPLACE VIEW */
+	AT_AddColumnToSequence,		/* implicitly via ALTER SEQUENCE USING */
 	AT_ColumnDefault,			/* alter column default */
 	AT_DropNotNull,				/* alter column drop not null */
 	AT_SetNotNull,				/* alter column set not null */
@@ -1294,6 +1295,7 @@ typedef enum AlterTableType
 	AT_SetStorage,				/* alter column set storage */
 	AT_DropColumn,				/* drop column */
 	AT_DropColumnRecurse,		/* internal to commands/tablecmds.c */
+	AT_DropColumnFromSequence,	/* implicitly via ALTER SEQUENCE USING */
 	AT_AddIndex,				/* add index */
 	AT_ReAddIndex,				/* internal to commands/tablecmds.c */
 	AT_AddConstraint,			/* add constraint */
@@ -2013,8 +2015,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2022,8 +2026,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 717f46b..16078c8 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -404,6 +404,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_default_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 6bd786d..c080487 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -48,10 +49,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
+ * functions, defined by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+	/* pg_am only */
 	FmgrInfo	aminsert;
 	FmgrInfo	ambeginscan;
 	FmgrInfo	amgettuple;
@@ -61,6 +64,14 @@ typedef struct RelationAmInfo
 	FmgrInfo	ammarkpos;
 	FmgrInfo	amrestrpos;
 	FmgrInfo	amcanreturn;
+	FmgrInfo	amcostestimate;
+
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+
+	/* Common */
+	FmgrInfo	amoptions;
 } RelationAmInfo;
 
 
@@ -131,23 +142,25 @@ typedef struct RelationData
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+
 	/*
-	 * index access support info (used only for an index relation)
+	 * Access support info (used only for index or sequence relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
 	 * arrays are indexed by support function number, which is a sufficient
 	 * identifier given that restriction.
 	 *
-	 * Note: rd_amcache is available for index AMs to cache private data about
-	 * an index.  This must be just a cache since it may get reset at any time
+	 * Note: rd_amcache is available for AMs to cache private data about
+	 * an object.  This must be just a cache since it may get reset at any time
 	 * (in particular, it will get reset by a relcache inval message for the
 	 * index).  If used, it must point to a single memory chunk palloc'd in
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
-	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am */
+	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am or pg_seqam */
 	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
@@ -158,7 +171,7 @@ typedef struct RelationData
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
-	void	   *rd_amcache;		/* available for use by index AM */
+	void	   *rd_amcache;		/* available for use by AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
 
 	/*
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index ba0b090..ac04502 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 7991e99..6b6493e 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -719,10 +719,10 @@ CREATE FUNCTION foo_mat(int,int) RETURNS setof foo_rescan_t AS 'begin for i in $
 --invokes ExecReScanFunctionScan - all these cases should materialize the function only once
 -- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function
 -- is on the inner path of a nestloop join
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) ON (r+i)<100;
@@ -739,10 +739,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) ON (r+i)<100;
  3 | 13 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
@@ -759,10 +759,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) WITH ORDINALITY
  3 | 13 | 3 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) ON (r+i)<100;
@@ -779,10 +779,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) ON (r+i)<100;
  3 | 13 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
@@ -799,10 +799,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) WITH ORDINALITY
  3 | 13 | 3 | 3
 (9 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( foo_sql(11,13), foo_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100;
@@ -876,10 +876,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) WITH O
 (9 rows)
 
 --invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL)
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13);
@@ -893,10 +893,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13) WITH ORDINALITY AS f(i,s,o);
@@ -910,10 +910,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 1
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r);
@@ -927,10 +927,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r) WITH ORDINALITY AS f(i,s,o);
@@ -944,10 +944,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 3
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2);
@@ -965,10 +965,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2);
  16 | 20 | 20 | 10
 (10 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2) WITH ORDINALITY AS f(i,s,o);
@@ -986,10 +986,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2) WITH ORD
  16 | 20 | 20 | 10 | 5
 (10 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13);
@@ -1003,10 +1003,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13) WITH ORDINALITY AS f(i,s,o);
@@ -1020,10 +1020,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 1
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r);
@@ -1037,10 +1037,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r);
  3 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r) WITH ORDINALITY AS f(i,s,o);
@@ -1054,10 +1054,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r) WITH ORDINALITY AS f(i
  3 | 13 | 6 | 3
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2);
@@ -1075,10 +1075,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2);
  16 | 20 | 20 | 10
 (10 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2) WITH ORDINALITY AS f(i,s,o);
@@ -1097,10 +1097,10 @@ SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2) WITH ORD
 (10 rows)
 
 -- selective rescan of multiple functions:
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_mat(10+r,13) );
@@ -1114,10 +1114,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_mat(10+r
  3 | 11 | 1 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(11,11) );
@@ -1131,10 +1131,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(11
  3 | 13 | 6 | 11 | 1
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(10+r,13) );
@@ -1148,10 +1148,10 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(10
  3 | 13 | 6 | 13 | 6
 (6 rows)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
- setval | setval 
---------+--------
-      1 |      1
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
+ pg_sequence_restore_state | pg_sequence_restore_state 
+---------------------------+---------------------------
+                           | 
 (1 row)
 
 SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( foo_sql(10+r1,13), foo_mat(10+r2,13) );
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index c7be273..d4e250f 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..ea45a2d 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_restore_state('sequence_test'::text, '99, false');
+ pg_sequence_restore_state 
+---------------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_restore_state('sequence_test'::regclass, '99, false');
+ pg_sequence_restore_state 
+---------------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | last_value | log_cnt | is_called 
+---------------+-------------+--------------+---------------------+-----------+-------------+-----------+------------+---------+-----------
+ foo_seq       |           1 |            1 | 9223372036854775807 |         1 |           1 | f         |          1 |       0 | f
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | last_value | log_cnt | is_called 
+---------------+-------------+--------------+---------------------+-----------+-------------+-----------+------------+---------+-----------
+ foo_seq       |           1 |            1 | 9223372036854775807 |         1 |           1 | f         |          2 |      31 | t
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 80c5706..d1bc3d7 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -100,14 +100,14 @@ SELECT table_name, column_name, is_updatable
  ro_view17  | b             | NO
  ro_view18  | a             | NO
  ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
  ro_view19  | start_value   | NO
  ro_view19  | increment_by  | NO
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
+ ro_view19  | last_value    | NO
+ ro_view19  | log_cnt       | NO
  ro_view19  | is_called     | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 470571b..b97670f 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -242,16 +242,16 @@ CREATE FUNCTION foo_mat(int,int) RETURNS setof foo_rescan_t AS 'begin for i in $
 -- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function
 -- is on the inner path of a nestloop join
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) ON (r+i)<100;
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_sql(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) ON (r+i)<100;
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN foo_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100;
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( foo_sql(11,13), foo_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100;
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN generate_series(11,13) f(i) ON (r+i)<100;
@@ -262,42 +262,42 @@ SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN unnest(array[10,20,30]) WITH O
 
 --invokes ExecReScanFunctionScan with chgParam != NULL (using implied LATERAL)
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(10+r,13) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_sql(11,10+r) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_sql(r1,r2) WITH ORDINALITY AS f(i,s,o);
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(10+r,13) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), foo_mat(11,10+r) WITH ORDINALITY AS f(i,s,o);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2);
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), foo_mat(r1,r2) WITH ORDINALITY AS f(i,s,o);
 
 -- selective rescan of multiple functions:
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(11,11), foo_mat(10+r,13) );
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(11,11) );
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( foo_sql(10+r,13), foo_mat(10+r,13) );
 
-SELECT setval('foo_rescan_seq1',1,false),setval('foo_rescan_seq2',1,false);
+SELECT pg_sequence_restore_state('foo_rescan_seq1','1,false'),pg_sequence_restore_state('foo_rescan_seq2','1,false');
 SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( foo_sql(10+r1,13), foo_mat(10+r2,13) );
 
 SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) f(i);
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..aa60bfd 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_restore_state('sequence_test'::text, '99, false');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_restore_state('sequence_test'::regclass, '99, false');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
-- 
1.9.1

0002-seqam-DDL-WIP.patchtext/x-diff; name=0002-seqam-DDL-WIP.patchDownload
>From d540f119373728d81ebdc9cb8563fc288e6a94d2 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 WIP

---
 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  | 329 +++++++++++++++++++++++++++++++++++
 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           |  12 ++
 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, 726 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 053da0a..ae682ab 100644
--- a/src/backend/access/sequence/seqam.c
+++ b/src/backend/access/sequence/seqam.c
@@ -68,8 +68,19 @@
 #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 "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
@@ -392,6 +403,324 @@ GetDefaultSeqAM(void)
 	return get_seqam_oid(default_seqam, false);
 }
 
+
+/*
+ * 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_amextracols:
+			/*
+			 * This function returns internal type so it has to also accept
+			 * internal on input even though there is actually no parameter
+			 * sent.
+			 */
+			nargs = 1;
+			retTypeId = INTERNALOID;
+			break;
+
+		case Anum_pg_seqam_amreloptions:
+			nargs = 2;
+			typeId[1] = BOOLOID;
+			retTypeId = BYTEAOID;
+			break;
+
+		case Anum_pg_seqam_aminit:
+			nargs = 5;
+			typeId[1] = INTERNALOID;
+			typeId[2] = BOOLOID;
+			typeId[3] = INTERNALOID;
+			typeId[4] = INTERNALOID;
+			retTypeId = VOIDOID;
+			break;
+
+		case Anum_pg_seqam_amalloc:
+			nargs = 4;
+			typeId[1] = INTERNALOID;
+			typeId[2] = INT8OID;
+			typeId[3] = INTERNALOID;
+			retTypeId = INT8OID;
+			break;
+
+		case Anum_pg_seqam_amsetval:
+			nargs = 3;
+			typeId[1] = INTERNALOID;
+			typeId[2] = INT8OID;
+			retTypeId = VOIDOID;
+			break;
+
+		case Anum_pg_seqam_amdump:
+			nargs = 2;
+			typeId[1] = INTERNALOID;
+			retTypeId = CSTRINGOID;
+			break;
+
+		case Anum_pg_seqam_amrestore:
+			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);
+}
+
+/*
+ * make pg_depend entries for a new pg_seqam entry
+ */
+static void
+makeSeqAMDependencies(HeapTuple tuple)
+{
+	Form_pg_seqam	seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+	ObjectAddress	myself,
+					referenced;
+
+	myself.classId = SeqAccessMethodRelationId;
+	myself.objectId = HeapTupleGetOid(tuple);
+	myself.objectSubId = 0;
+
+	/* dependency on extension */
+	recordDependencyOnCurrentExtension(&myself, false);
+
+	/* dependencies on functions */
+	referenced.classId = ProcedureRelationId;
+	referenced.objectSubId = 0;
+
+	referenced.objectId = seqam->seqamextracols;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	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 *parameters)
+{
+	char	   *seqamname = strVal(linitial(names));
+	Oid			seqamoid;
+	ListCell   *pl;
+	Relation	rel;
+	Datum		values[Natts_pg_seqam];
+	bool		nulls[Natts_pg_seqam];
+	HeapTuple	tuple;
+
+	/* 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_amname - 1] =
+		DirectFunctionCall1(namein, CStringGetDatum(seqamname));
+
+	/*
+	 * loop over the definition list and extract the information we need.
+	 */
+	foreach(pl, parameters)
+	{
+		DefElem    *defel = (DefElem *) lfirst(pl);
+
+		if (pg_strcasecmp(defel->defname, "extracolumns") == 0)
+		{
+			values[Anum_pg_seqam_amextracols - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_amextracols);
+		}
+		else if (pg_strcasecmp(defel->defname, "reloptions") == 0)
+		{
+			values[Anum_pg_seqam_amreloptions - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_amreloptions);
+		}
+		else if (pg_strcasecmp(defel->defname, "init") == 0)
+		{
+			values[Anum_pg_seqam_aminit - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_aminit);
+		}
+		else if (pg_strcasecmp(defel->defname, "alloc") == 0)
+		{
+			values[Anum_pg_seqam_amalloc - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_amalloc);
+		}
+		else if (pg_strcasecmp(defel->defname, "setval") == 0)
+		{
+			values[Anum_pg_seqam_amsetval - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_amsetval);
+		}
+		else if (pg_strcasecmp(defel->defname, "dump") == 0)
+		{
+			values[Anum_pg_seqam_amdump - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_amdump);
+		}
+		else if (pg_strcasecmp(defel->defname, "restore") == 0)
+		{
+			values[Anum_pg_seqam_amrestore - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_amrestore);
+		}
+		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_amextracols - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method extracolumns function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_amreloptions - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method reloptions function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_aminit - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method init function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_amalloc - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method alloc function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_amsetval - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method setval function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_amdump - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method dump function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_amrestore - 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);
+
+	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
  *
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..58cf813 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_amname,
+		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 5834b2d..fdf8465 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1530,9 +1530,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 976eb6b..fa37899 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 definition
+				{
+					DefineStmt *n = makeNode(DefineStmt);
+					n->kind = OBJECT_SEQAM;
+					n->args = NIL;
+					n->defnames = list_make1(makeString($6));
+					n->definition = $7;
+					$$ = (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..c8e1876 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,11 @@ ProcessUtilitySlow(Node *parsetree,
 							Assert(stmt->args == NIL);
 							DefineCollation(stmt->defnames, stmt->definition);
 							break;
+						case OBJECT_SEQAM:
+							Assert(stmt->args == NIL);
+							Assert(list_length(stmt->defnames) == 1);
+							DefineSeqAM(stmt->defnames, stmt->definition);
+							break;
 						default:
 							elog(ERROR, "unrecognized define stmt type: %d",
 								 (int) stmt->kind);
@@ -1960,6 +1966,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 +2065,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 c4f6f73..b8b3b27 100644
--- a/src/include/access/seqam.h
+++ b/src/include/access/seqam.h
@@ -40,6 +40,9 @@ extern char *default_seqam;
 
 extern Oid GetDefaultSeqAM(void);
 
+extern Oid DefineSeqAM(List *names, List *parameters);
+extern void RemoveSeqAMById(Oid seqamoid);
+
 extern List *seqam_extra_columns(Oid seqamid);
 extern void seqam_init(Oid seqamid, List *seqparams,
 					   List *reloptions, bool is_init,
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 0b6e148..517035f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1817,9 +1817,9 @@ DATA(insert OID = 1576 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 20 "
 DESCR("set sequence value");
 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

0003-gapless-sequence-v2.patchtext/x-diff; name=0003-gapless-sequence-v2.patchDownload
>From ab8e7fb0f6b96b645a34f81979d7f3cade55c231 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Tue, 9 Dec 2014 22:57:53 +0100
Subject: [PATCH 3/3] gapless sequence v2

---
 contrib/gapless_seq/Makefile                 |  65 +++++++
 contrib/gapless_seq/expected/concurrency.out |  31 +++
 contrib/gapless_seq/expected/gapless_seq.out |  81 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  57 ++++++
 contrib/gapless_seq/gapless_seq.c            | 274 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/gapless_seq.sgml         |  22 +++
 contrib/gapless_seq/specs/concurrency.spec   |  29 +++
 contrib/gapless_seq/sql/gapless_seq.sql      |  38 ++++
 9 files changed, 603 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/gapless_seq.sgml
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql

diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..63b51c4
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,65 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-install=./tmp_check \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq
+	$(pg_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq
+	$(pg_isolation_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..903ed30
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,81 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+ALTER SEQUENCE test_alter_seq START 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+ERROR:  cannot drop sequence access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on sequence access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..8fd46e1
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,57 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_extracols(INTERNAL)
+RETURNS INTERNAL
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_init(INTERNAL, INTERNAL, BOOLEAN, INTERNAL, INTERNAL)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_alloc(INTERNAL, INTERNAL, BIGINT, INTERNAL)
+RETURNS BIGINT
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_setval(INTERNAL, INTERNAL, BIGINT)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_dump(INTERNAL, INTERNAL)
+RETURNS CSTRING
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_restore(INTERNAL, INTERNAL, CSTRING)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+DELETE FROM pg_catalog.pg_seqam
+WHERE seqamname = 'gapless';
+
+CREATE ACCESS METHOD FOR SEQUENCES gapless (
+	extracolumns = seqam_gapless_extracols,	/* extra columns for gapless sequence */
+	reloptions = seqam_local_reloptions,	/* reloptions parser is same as local (no reloptions) */
+	init = seqam_gapless_init,				/* init new gapless sequence */
+	alloc = seqam_gapless_alloc,			/* logs and returns each value... slow */
+	setval = seqam_gapless_setval,			/* setval support */
+	dump = seqam_gapless_dump,				/* pgdump support */
+	restore = seqam_gapless_restore			/* pgdump support */
+);
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..2e3a27a
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = Gapless functions
+ *
+ *------------------------------------------------------------
+ */
+extern Datum seqam_gapless_extracols(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_init(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_dump(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_restore(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_extracols);
+PG_FUNCTION_INFO_V1(seqam_gapless_init);
+PG_FUNCTION_INFO_V1(seqam_gapless_alloc);
+PG_FUNCTION_INFO_V1(seqam_gapless_setval);
+PG_FUNCTION_INFO_V1(seqam_gapless_dump);
+PG_FUNCTION_INFO_V1(seqam_gapless_restore);
+
+
+typedef struct FormGaplessSequence
+{
+	FormData_pg_sequence seq;
+	int64		tx_start_value;
+	int64		tx_last_value;
+	uint32		xid;
+} FormGaplessSequence;
+
+SeqAMColumnData seqam_gapless_cols[] = {
+	{"tx_start_value", INT8OID, -1, true},
+	{"tx_last_value", INT8OID, -1, true},
+	{"xid", XIDOID, -1, true}
+};
+#define SEQAM_GAPLESS_EXTRACOLS_CNT 3
+#define SEQAM_GAPLESS_COL_TXSTARTVAL SEQ_COL_LASTCOL + 1
+#define SEQAM_GAPLESS_COL_TXLASTVAL SEQ_COL_LASTCOL + 2
+#define SEQAM_GAPLESS_COL_XID SEQ_COL_LASTCOL + 3
+
+
+/*
+ * seqam_gapless_extracols()
+ *
+ * Get definitions for extra columns needed by a gapless sequence
+ */
+Datum
+seqam_gapless_extracols(PG_FUNCTION_ARGS)
+{
+	List *ret = NIL;
+	int i;
+
+	for (i = 0; i < SEQAM_GAPLESS_EXTRACOLS_CNT; i++)
+		ret = lappend(ret, &seqam_gapless_cols[i]);
+
+	PG_RETURN_POINTER(ret);
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ * TODO: more checks
+ */
+Datum
+seqam_gapless_init(PG_FUNCTION_ARGS)
+{
+	List *params = (List *)(PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));
+	bool is_init = PG_GETARG_BOOL(2);
+	Datum *values = (Datum *)PG_GETARG_POINTER(3);
+	bool *nulls = (bool *)PG_GETARG_POINTER(4);
+
+	ListCell   *param;
+
+	foreach(param, params)
+	{
+		DefElem    *defel = (DefElem *) lfirst(param);
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("RESTART is not supported for gapless sequences")));
+			break;
+		}
+	}
+
+	if (is_init)
+	{
+		values[SEQAM_GAPLESS_COL_TXSTARTVAL - 1] = values[SEQ_COL_STARTVAL - 1];
+		values[SEQAM_GAPLESS_COL_TXLASTVAL - 1] = values[SEQ_COL_STARTVAL - 1];
+		values[SEQAM_GAPLESS_COL_XID - 1] = UInt32GetDatum(InvalidTransactionId);
+
+		nulls[SEQAM_GAPLESS_COL_TXSTARTVAL - 1] = false;
+		nulls[SEQAM_GAPLESS_COL_TXLASTVAL - 1] = false;
+		nulls[SEQAM_GAPLESS_COL_XID - 1] = false;
+	}
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+Datum
+seqam_gapless_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	/* we ignore nreguested as gapless sequence can't do caching */
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	int64		result;
+	FormGaplessSequence *seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	while (seq->xid != local_xid && TransactionIdIsInProgress(seq->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seq->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	}
+
+	/*
+	 * Only increment if this is same transaction which consumed the sequence
+	 * previously (i.e. we are doing multiple nextval calls in same
+	 * transaction) or if the transaction that previously consumed this
+	 * sequence committed sucessfully.
+	 */
+	if (seq->xid == local_xid ||
+		TransactionIdDidCommit(seq->xid))
+	{
+		result = seq->tx_last_value;
+		(void) sequence_increment(seqrel, &result, 1, seq->seq.min_value,
+								  seq->seq.max_value, seq->seq.increment_by,
+								  seq->seq.is_cycled, true);
+	}
+	else
+	{
+		result = seq->tx_start_value;
+	}
+
+	if (seq->xid == local_xid)
+	{
+		seq->tx_last_value = result;
+	}
+	else
+	{
+		/*
+		 * Remember current xid and starting value of the sequence in this
+		 * transaction so that in case the transaction rolls back the
+		 * subsequent nextval call will know what is the real first unallocated
+		 * value.
+		 */
+		seq->tx_last_value = seq->tx_start_value = result;
+		seq->xid = local_xid;
+	}
+
+	*last = result;
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+Datum
+seqam_gapless_setval(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_dump()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ *
+ * Format is: '<last_value::bigint>'
+ *
+ * TODO: locking
+ */
+Datum
+seqam_gapless_dump(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	FormGaplessSequence *seq;
+	char		buf[19 + 1]; /* int64 + null byte */
+	char	   *result;
+
+
+	seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	pg_lltoa(seq->tx_last_value, buf);
+	result = pstrdup(buf);
+
+	PG_RETURN_CSTRING(result);
+}
+
+/*
+ * seqam_gapless_restore()
+ *
+ * Restore previously dumpred state of gapless sequence
+ *
+ * Format is: '<last_value::bigint>'
+ *
+ * TODO: locking
+*/
+Datum
+seqam_gapless_restore(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			   *state = PG_GETARG_CSTRING(2);
+	FormGaplessSequence *seq;
+	int64				last_value;
+
+	scanint8(state, false, &last_value);
+
+	seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->tx_last_value = last_value;
+	seq->xid = InvalidTransactionId;
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..326601b
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = pg_catalog
diff --git a/contrib/gapless_seq/gapless_seq.sgml b/contrib/gapless_seq/gapless_seq.sgml
new file mode 100644
index 0000000..3eb536f
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.sgml
@@ -0,0 +1,22 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless_seq" xreflabel="gapless_seq">
+ <title>gapless sequence</title>
+
+ <indexterm zone="gapless_seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence.
+  This could be very costly and is not recommended for general
+  usage except in specific applications that require this feature.
+ </para>
+
+</sect1>
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..2ea191e
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,38 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq START 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
-- 
1.9.1

#56Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Petr Jelinek (#55)
Re: Sequence Access Method WIP

On 12.1.2015 22:33, Petr Jelinek wrote:

On 15/12/14 11:36, Petr Jelinek wrote:

On 10/12/14 03:33, Petr Jelinek wrote:

On 24/11/14 12:16, Heikki Linnakangas wrote:

About the rough edges:
- The AlterSequence is not prettiest code around as we now have to
create new relation when sequence AM is changed and I don't know how to
do that nicely
- I am not sure if I did the locking, command order and dependency
handling in AlterSequence correcly

This version does AlterSequence differently and better. Didn't attach
the gapless sequence again as that one is unchanged.

And another version, separated into patch-set of 3 patches.
First patch contains the seqam patch itself, not many changes there,
mainly docs/comments related. What I wrote in the previous email for
version 3 still applies.

I did a review of the first part today - mostly by reading through the
diff. I plan to do a bit more thorough testing in a day or two. I'll
also look at the two (much smaller) patches.

comments/questions/general nitpicking:

(1) Why treating empty string as equal to 'local'? Isn't enforcing the
actual value a better approach?

(2) NITPICK: Maybe we could use access_method in the docs (instead of
sequence_access_method), as the 'sequence' part is clear from the
context?

(3) Why index_reloptions / sequence_reloptions when both do exactly the
same thing (call to common_am_reloptions)? I'd understand this if
the patch(es) then change the sequence_reloptions() but that's not
happening. Maybe that's expected to happen?

(4) DOCS: Each sequence can only use one access method at a time.

Does that mean a sequence can change the access method during it's
lifetime? My understanding is that the AM is fixed after creating
the sequence?

(5) DOCS/NITPICK: SeqAM / SeqAm ... (probably should be the same?)

(6) cache lookup failed for relation %u

I believe this should reference 'sequence access method' instead of
a relation.

(7) seqam_init

I believe this is a bug (not setting nulls[4] but twice nulls[3]):

+	fcinfo.argnull[0] = seqparams == NULL;
+	fcinfo.argnull[1] = reloptions_parsed == NULL;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[3] = false;

(8) check_default_seqam without a transaction

* If we aren't inside a transaction, we cannot do database access so
* cannot verify the name. Must accept the value on faith.

In which situation this happens? Wouldn't it be better to simply
enforce the transaction and fail otherwise?

regards

--
Tomas Vondra http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#57Petr Jelinek
petr@2ndquadrant.com
In reply to: Tomas Vondra (#56)
Re: Sequence Access Method WIP

On 13/01/15 13:24, Tomas Vondra wrote:

On 12.1.2015 22:33, Petr Jelinek wrote:

On 15/12/14 11:36, Petr Jelinek wrote:

On 10/12/14 03:33, Petr Jelinek wrote:

On 24/11/14 12:16, Heikki Linnakangas wrote:

About the rough edges:
- The AlterSequence is not prettiest code around as we now have to
create new relation when sequence AM is changed and I don't know how to
do that nicely
- I am not sure if I did the locking, command order and dependency
handling in AlterSequence correcly

This version does AlterSequence differently and better. Didn't attach
the gapless sequence again as that one is unchanged.

And another version, separated into patch-set of 3 patches.
First patch contains the seqam patch itself, not many changes there,
mainly docs/comments related. What I wrote in the previous email for
version 3 still applies.

I did a review of the first part today - mostly by reading through the
diff. I plan to do a bit more thorough testing in a day or two. I'll
also look at the two (much smaller) patches.

Thanks!

comments/questions/general nitpicking:

(1) Why treating empty string as equal to 'local'? Isn't enforcing the
actual value a better approach?

Álvaro mentioned on IM also, I already changed it to saner normal GUC
with 'local' as default value in my working copy

(2) NITPICK: Maybe we could use access_method in the docs (instead of
sequence_access_method), as the 'sequence' part is clear from the
context?

Yes.

(3) Why index_reloptions / sequence_reloptions when both do exactly the
same thing (call to common_am_reloptions)? I'd understand this if
the patch(es) then change the sequence_reloptions() but that's not
happening. Maybe that's expected to happen?

That's leftover from the original design where AM was supposed to call
this, since this is not exposed to AM anymore I think it can be single
function now.

(4) DOCS: Each sequence can only use one access method at a time.

Does that mean a sequence can change the access method during it's
lifetime? My understanding is that the AM is fixed after creating
the sequence?

Oh, I forgot to add ALTER SEQUENCE USING into docs, you can change AM
even though you probably don't want to do it often, but for migrations
it's useful.

(8) check_default_seqam without a transaction

* If we aren't inside a transaction, we cannot do database access so
* cannot verify the name. Must accept the value on faith.

In which situation this happens? Wouldn't it be better to simply
enforce the transaction and fail otherwise?

Reading postgresql.conf during startup, I don't think we want to fail in
that scenario ;)

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#58Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Petr Jelinek (#55)
Re: Sequence Access Method WIP

On 01/12/2015 11:33 PM, Petr Jelinek wrote:

Second patch adds DDL support. I originally wanted to make it
CREATE/DROP SEQUENCE ACCESS METHOD... but that would mean making ACCESS
a reserver keyword so I went for CREATE ACCESS METHOD FOR SEQUENCES
which does not need to change anything (besides adding METHOD to
unreserved keywords).
The DDL support uses the DefineStmt infra with some very small change as
the sequence ams are not schema qualified, but I think it's acceptable
and saves considerable amount of boilerplate.

Do we need DDL commands for this at all? I could go either way on that
question. We recently had a discussion on that wrt. index access methods
[1]: /messages/by-id/26822.1414516012@sss.pgh.pa.us
is not worth it. The extension can just insert the rows into pg_seqam
with INSERT. Do we expect sequence access methods as extensions to be
more popular than index access methods? Maybe, because the WAL-logging
problem doesn't exist. But OTOH, if you're writing something like a
replication system that needs global sequences as part of it, there
aren't that many of those, and the installation scripts will need to
deal with more complicated stuff than inserting a row in pg_seqam.

[1]: /messages/by-id/26822.1414516012@sss.pgh.pa.us

And third patch is gapless sequence implementation updated to work with
the new DDL support with some tests added for checking if dependencies
work correctly. It also acts as example on how to make custom AMs.

I'll take a look at that to see how the API works, but we're not going
to include it in the source tree, because it doesn't actually guarantee
gaplessness. That makes it a pretty dangerous example. I'm sure we can
come up with a better example that might even be useful. How about a
Lamport's clock sequence, which advances once per second, in addition to
when anyone calls nextval() ? Or a remote sequence that uses an FDW to
call nextval() in a foreign server?

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#59Robert Haas
robertmhaas@gmail.com
In reply to: Heikki Linnakangas (#58)
Re: Sequence Access Method WIP

On Thu, Jan 22, 2015 at 10:50 AM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

On 01/12/2015 11:33 PM, Petr Jelinek wrote:

Second patch adds DDL support. I originally wanted to make it
CREATE/DROP SEQUENCE ACCESS METHOD... but that would mean making ACCESS
a reserver keyword so I went for CREATE ACCESS METHOD FOR SEQUENCES
which does not need to change anything (besides adding METHOD to
unreserved keywords).
The DDL support uses the DefineStmt infra with some very small change as
the sequence ams are not schema qualified, but I think it's acceptable
and saves considerable amount of boilerplate.

Do we need DDL commands for this at all? I could go either way on that
question. We recently had a discussion on that wrt. index access methods
[1], and Tom opined that providing DDL for creating index access methods is
not worth it. The extension can just insert the rows into pg_seqam with
INSERT. Do we expect sequence access methods as extensions to be more
popular than index access methods? Maybe, because the WAL-logging problem
doesn't exist. But OTOH, if you're writing something like a replication
system that needs global sequences as part of it, there aren't that many of
those, and the installation scripts will need to deal with more complicated
stuff than inserting a row in pg_seqam.

I think the main reason we don't need DDL for pg_am is because there's
no real hope of anybody actually inserting anything in there whether
we have a command for it or not. If we actually expect this to be
used, I think it should probably have some kind of DDL, because
otherwise what will pg_dump emit? "Nothing" is bad because then dumps
won't restore, and "direct inserts to pg_seqam" doesn't seem great
either.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#60Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#58)
Re: Sequence Access Method WIP

On 22/01/15 16:50, Heikki Linnakangas wrote:

On 01/12/2015 11:33 PM, Petr Jelinek wrote:

Second patch adds DDL support. I originally wanted to make it
CREATE/DROP SEQUENCE ACCESS METHOD... but that would mean making ACCESS
a reserver keyword so I went for CREATE ACCESS METHOD FOR SEQUENCES
which does not need to change anything (besides adding METHOD to
unreserved keywords).
The DDL support uses the DefineStmt infra with some very small change as
the sequence ams are not schema qualified, but I think it's acceptable
and saves considerable amount of boilerplate.

Do we need DDL commands for this at all? I could go either way on that
question. We recently had a discussion on that wrt. index access methods
[1], and Tom opined that providing DDL for creating index access methods
is not worth it. The extension can just insert the rows into pg_seqam
with INSERT. Do we expect sequence access methods as extensions to be
more popular than index access methods? Maybe, because the WAL-logging
problem doesn't exist. But OTOH, if you're writing something like a
replication system that needs global sequences as part of it, there
aren't that many of those, and the installation scripts will need to
deal with more complicated stuff than inserting a row in pg_seqam.

I don't know about popularity, and I've seen the discussion about
indexes. The motivation for DDL for me was handling dependencies
correctly, that's all. If we say we don't care about that (and allow
DROP EXTENSION even though user has sequences that are using the AM)
then we don't need DDL.

[1] /messages/by-id/26822.1414516012@sss.pgh.pa.us

And third patch is gapless sequence implementation updated to work with
the new DDL support with some tests added for checking if dependencies
work correctly. It also acts as example on how to make custom AMs.

I'll take a look at that to see how the API works, but we're not going
to include it in the source tree, because it doesn't actually guarantee
gaplessness. That makes it a pretty dangerous example. I'm sure we can
come up with a better example that might even be useful. How about a
Lamport's clock sequence, which advances once per second, in addition to
when anyone calls nextval() ? Or a remote sequence that uses an FDW to
call nextval() in a foreign server?

I have updated patch ready, just didn't submit it because I am otherwise
busy this week, I hope to get to it today evening or tomorrow morning,
so I'd wait until that with looking at the patch.

The new version (the one that is not submitted yet) of gapless sequence
is way more ugly and probably not best example either but does guarantee
gaplessness (it stores the last value in it's own value table). So I am
not sure if it should be included either...

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#61Petr Jelinek
petr@2ndquadrant.com
In reply to: Petr Jelinek (#60)
3 attachment(s)
Re: Sequence Access Method WIP

On 22/01/15 17:02, Petr Jelinek wrote:

The new version (the one that is not submitted yet) of gapless sequence
is way more ugly and probably not best example either but does guarantee
gaplessness (it stores the last value in it's own value table). So I am
not sure if it should be included either...

Here it is as promised.

Notable changes:
- The gapless sequence rewritten to use the transactional storage as
that's the only way to guarantee gaplessness between dump and restore.

- Custom columns are now stored in the catalog instead of generated by
some special interface. This makes it possible to record dependencies on
types and also seems in line with how other similar things are done.

- Removed couple of pallocs from the nextval codepath which improved
performance considerably.

- Changed how the ALTER SEQUENCE ... USING behaves - when RESET WITH
value is specified the new sequenceAM will use that one for starting, if
it's not specified the nextval from original sequenceAM will be used as
RESET value. In general handling of the RESET parameter is left to the AMs.

- Unified the reloptions for index and sequences into single function.

- Re-added setval3 for compatibility reasons - it only works on local
sequences, otherwise it throws error.

- Made the 'local' default value for the GUC instead of special handling
of the empty string.

Notable missing things:
- Docs for pg_seqam.

- pg_dump support for pg_seqam. It's embarrassing but I didn't think
about this until Robert mentioned it, I am apparently too used to handle
these things using extensions.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-seqam-v6.patchtext/x-diff; name=0001-seqam-v6.patchDownload
>From 6f0ff83f7a4d1755e2c82cdc03fe4807956320c7 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Mon, 22 Sep 2014 19:22:40 +0200
Subject: [PATCH 1/3] seqam v6

---
 doc/src/sgml/config.sgml                      |   22 +
 doc/src/sgml/ref/alter_sequence.sgml          |   19 +
 doc/src/sgml/ref/create_sequence.sgml         |   11 +
 src/backend/access/Makefile                   |    3 +-
 src/backend/access/common/reloptions.c        |   12 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqam.c           |  430 ++++++++
 src/backend/access/sequence/seqlocal.c        |  363 +++++++
 src/backend/catalog/Makefile                  |    2 +-
 src/backend/catalog/objectaddress.c           |    1 +
 src/backend/commands/indexcmds.c              |    2 +-
 src/backend/commands/sequence.c               | 1423 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |   31 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   78 +-
 src/backend/parser/parse_utilcmd.c            |    1 +
 src/backend/utils/cache/catcache.c            |    6 +-
 src/backend/utils/cache/relcache.c            |   62 +-
 src/backend/utils/cache/syscache.c            |   23 +
 src/backend/utils/misc/guc.c                  |   12 +
 src/backend/utils/misc/postgresql.conf.sample |    1 +
 src/bin/pg_dump/pg_dump.c                     |   73 +-
 src/bin/psql/describe.c                       |   95 +-
 src/include/access/reloptions.h               |    7 +-
 src/include/access/seqam.h                    |   68 ++
 src/include/catalog/indexing.h                |    5 +
 src/include/catalog/pg_proc.h                 |   18 +
 src/include/catalog/pg_seqam.h                |   80 ++
 src/include/commands/sequence.h               |   23 +-
 src/include/nodes/parsenodes.h                |   10 +-
 src/include/utils/guc.h                       |    1 +
 src/include/utils/rel.h                       |   25 +-
 src/include/utils/syscache.h                  |    2 +
 src/test/regress/expected/sanity_check.out    |    1 +
 src/test/regress/expected/sequence.out        |   28 +-
 src/test/regress/expected/updatable_views.out |    4 +-
 src/test/regress/sql/sequence.sql             |    4 +-
 38 files changed, 2292 insertions(+), 679 deletions(-)
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqam.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqam.h
 create mode 100644 src/include/catalog/pg_seqam.h

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6bcb106..d262795 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5607,6 +5607,28 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-default-sequenceam" xreflabel="default_sequenceam">
+      <term><varname>default_sequenceam</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>default_sequenceam</> configuration parameter</primary>
+      </indexterm>
+      <indexterm><primary>sequence access method</><secondary>default</></>
+      </term>
+      <listitem>
+       <para>
+        This variable specifies the default sequence access method to be used
+        for <type>SERIAL</> type and when a <command>CREATE SEQUENCE</> command
+        is executed without <command>USING</> clause.
+       </para>
+
+       <para>
+        The default is 'local' sequence access method. If the value does not
+        match the name of any existing sequence access method, an error will be
+        raised.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-check-function-bodies" xreflabel="check_function_bodies">
       <term><varname>check_function_bodies</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 9ba9bc4..a5ee41c 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,6 +29,7 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
@@ -221,6 +222,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..30014af 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,7 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +224,16 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </refsect1>
 
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..818da15 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+			  transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index f008fab..d8d7b4a 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -821,7 +821,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -852,7 +853,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+			options = am_reloptions(amoptions, datum, false);
+			break;
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1298,14 +1302,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..7896d0f
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,430 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module.  The on-disk format of
+ * Sequences has common part for all AMs, but each AM can specify extra columns
+ * used for storage of its private data. The storage itself is abstracted from
+ * AM and done by backend code.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAM data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*default_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_aminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Method API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_extra_columns - get list of extra columns needed by the am
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_dump_state	- dump sequence state (for pg_dump)
+ *		seqam_restore_state - restore sequence state (for pg_dump)
+ *
+ * Additionaly, the am_reloptions interface is used for parsing reloptions
+ * that can be used for passing AM specific options.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_extra_columns - get custom column list
+ */
+int
+seqam_extra_columns(Oid seqamid, Oid **coltypes, char ***colnames)
+{
+	HeapTuple	tuple = NULL;
+	int			ncols;
+	bool		isnull;
+	ArrayType  *arr;
+	Datum	   *elems;
+	int			nelems;
+	int			i;
+	Datum		seqamcoltypes,
+				seqamcolnames;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 seqamid);
+
+	seqamcoltypes = SysCacheGetAttr(SEQAMOID, tuple,
+									Anum_pg_seqam_seqamcoltypes, &isnull);
+	if (isnull)
+		return 0;
+
+	seqamcolnames = SysCacheGetAttr(SEQAMOID, tuple,
+									Anum_pg_seqam_seqamcolnames, &isnull);
+	if (isnull)
+		return 0;
+
+	/* Copy the column types array to out parameter. */
+	arr = DatumGetArrayTypeP(seqamcoltypes);
+	ncols = ARR_DIMS(arr)[0];
+	*coltypes = (Oid *) palloc(ncols * sizeof(Oid));
+	memcpy(*coltypes, ARR_DATA_PTR(arr), ncols * sizeof(Oid));
+	if (ARR_NDIM(arr) != 1 ||
+		ncols < 0 ||
+		ARR_HASNULL(arr) ||
+		ARR_ELEMTYPE(arr) != OIDOID)
+		elog(ERROR, "seqamcoltypes is not a 1-D Oid array");
+
+	/* Copy the column names array to out parameter. */
+	deconstruct_array(DatumGetArrayTypeP(seqamcolnames), TEXTOID, -1, false,
+					  'i', &elems, NULL, &nelems);
+
+	if (nelems != ncols)	/* should not happen */
+		elog(ERROR, "seqamcolnames and seqamcoltypes must have the same number of elements");
+
+	*colnames = (char **) palloc(sizeof(char *) * ncols);
+		for (i = 0; i < ncols; i++)
+			(*colnames)[i] = TextDatumGetCString(elems[i]);
+
+	ReleaseSysCache(tuple);
+
+	return ncols;
+}
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+extern void
+seqam_init(Oid seqamid, Oid seqrelid, List *seqparams, List *reloptions,
+		   Datum *values, bool *nulls)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple = NULL;
+	Form_pg_seqam seqamForm;
+	FunctionCallInfoData fcinfo;
+	char	   *validnsps[] = {NULL, NULL};
+	Datum       reloptions_transformed;
+	bytea	   *reloptions_parsed;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	fmgr_info(seqamForm->seqaminit, &procedure);
+
+	/* Allow am specific options */
+	validnsps[0] = NameStr(seqamForm->seqamname);
+
+	/*
+	 *  Parse AM-specific options, convert to text array form,
+	 *  retrieve the AM-option function and then validate.
+	 */
+	reloptions_transformed = transformRelOptions((Datum) NULL, reloptions,
+												 NULL, validnsps, false,
+												 false);
+
+	reloptions_parsed = am_reloptions(seqamForm->seqamreloptions,
+									  reloptions_transformed, true);
+
+	/*
+	 * Have the seqam's proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = ObjectIdGetDatum(seqrelid);
+	fcinfo.arg[1] = PointerGetDatum(seqparams);
+	fcinfo.arg[2] = PointerGetDatum(reloptions_parsed);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.arg[4] = PointerGetDatum(nulls);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[4] = false;
+
+	FunctionCallInvoke(&fcinfo);
+
+	ReleaseSysCache(tuple);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(nrequested);
+	fcinfo.arg[3] = PointerGetDatum(last);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 3, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(new_value);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * seqam_dump_state - pg_dump support
+ */
+char *
+seqam_dump_state(Relation seqrel, SequenceHandle *seqh)
+{
+	FmgrInfo	procedure;
+	Datum		ret;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamdump, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 2, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+
+	return DatumGetCString(ret);
+}
+
+/*
+ * seqam_restore_state - restore from pg_dump
+ */
+void
+seqam_restore_state(Relation seqrel, SequenceHandle *seqh, char *state)
+{
+	FmgrInfo	procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamrestore, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = CStringGetDatum(state);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new default_sequenceam */
+bool
+check_default_seqam(char **newval, void **extra, GucSource source)
+{
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.  Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence access method \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence access method \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..c92ea38
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,363 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct FormLocalSequence
+{
+	FormData_pg_sequence seq;
+	int64		last_value;
+	int64		log_cnt;
+	bool		is_called;
+} FormLocalSequence;
+
+#define SEQAM_LOCAL_COL_LASTVAL (SEQ_COL_LASTCOL + 1)
+#define SEQAM_LOCAL_COL_LOGCNT (SEQ_COL_LASTCOL + 2)
+#define SEQAM_LOCAL_COL_CALLED (SEQ_COL_LASTCOL + 3)
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum       reloptions = PG_GETARG_DATUM(0);
+	bool        validate = PG_GETARG_BOOL(1);
+	bytea      *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	Oid		seqrelid = PG_GETARG_OID(0);
+	List   *seqoptions = (List *) PG_GETARG_POINTER(1);
+	Datum  *values = (Datum *) PG_GETARG_POINTER(3);
+	bool   *nulls = (bool *) PG_GETARG_POINTER(4);
+	bool	found_restart;
+	int64	start_value,
+			last_value,
+			min_value,
+			max_value;
+
+	/* Get the new value to use as starting point. */
+	start_value = DatumGetInt64(values[SEQ_COL_STARTVAL - 1]);
+	start_value = sequence_get_restart_value(seqoptions, start_value,
+											 &found_restart);
+
+	/*
+	 * If this is new sequence or restart was provided, use starting value,
+	 * otherwise work with our saved value.
+	 */
+	if (seqrelid == InvalidOid || found_restart)
+		last_value = start_value;
+	else
+		last_value = DatumGetInt64(values[SEQAM_LOCAL_COL_LASTVAL - 1]);
+
+	/* Validate the min/max against the starting point. */
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+	sequence_check_range(last_value, min_value, max_value);
+
+	/*
+	 * If this is new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (seqrelid == InvalidOid || found_restart)
+	{
+		nulls[SEQAM_LOCAL_COL_LASTVAL - 1] = false;
+		nulls[SEQAM_LOCAL_COL_CALLED - 1] = false;
+		values[SEQAM_LOCAL_COL_LASTVAL - 1] = Int64GetDatum(last_value);
+		values[SEQAM_LOCAL_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+
+	/* We always reset the log_cnt. */
+	values[SEQAM_LOCAL_COL_LOGCNT - 1] = Int64GetDatum(0);
+	nulls[SEQAM_LOCAL_COL_LOGCNT - 1] = false;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormLocalSequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				logit = false;
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	next = result = seq->last_value;
+	incby = seq->seq.increment_by;
+	maxv = seq->seq.max_value;
+	minv = seq->seq.min_value;
+	is_cycled = seq->seq.is_cycled;
+	fetch = nrequested;
+	log = seq->log_cnt;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!seq->is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !seq->is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (seq->is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	START_CRIT_SECTION();
+
+	/* Log our cached data. */
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		seq->log_cnt = 0;
+
+		sequence_save_tuple(seqh, NULL, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	seq->log_cnt = log;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, false);
+
+	END_CRIT_SECTION();
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	*seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    next = PG_GETARG_INT64(2);
+	FormLocalSequence *seq;
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	seq->log_cnt = 0;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Format the state of the local sequence into the dump format.
+ *
+ * Shared code for seqam_local_dump and setval3_oid.
+ */
+char *
+seqam_local_format_state(int64 value, bool is_called)
+{
+	/* int64 + colon + space + t/f + null byte */
+#define SEQAM_LOCAL_STATE_LEN (19 + 1 + 1 + 1 + 1)
+	char *state = (char *) palloc0(SEQAM_LOCAL_STATE_LEN);
+
+	snprintf(state, SEQAM_LOCAL_STATE_LEN, INT64_FORMAT", %s",
+			 value, is_called ? "t" : "f");
+
+	return state;
+}
+
+/*
+ * seqam_local_dump()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ *
+ * Format is: '<last_value::bigint>, <is_called::boolean>'
+ */
+Datum
+seqam_local_dump(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	FormLocalSequence  *seq;
+	char			   *result;
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	result = seqam_local_format_state(seq->last_value, seq->is_called);
+
+	PG_RETURN_CSTRING(result);
+}
+
+/*
+ * seqam_local_restore()
+ *
+ * Restore previously dumpred state of local sequence (used by pg_dump)
+ *
+ * Format is: '<last_value::bigint>, <is_called::boolean>'
+*/
+Datum
+seqam_local_restore(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			   *state = PG_GETARG_CSTRING(2);
+	FormLocalSequence  *seq;
+	int64				last_value;
+	bool				is_called;
+	char			   *buf;
+	char			   *ptr = state;
+	int					len;
+
+	/* Check that we can find comma. */
+	while (*ptr != ',' && *ptr != '\0')
+		ptr++;
+
+	if (*ptr == '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for local sequence state: \"%s\"",
+						state)));
+
+	/*
+	 * Copy the part of the string before comma into work buffer
+	 * for scanint8, it will handle leading/trailing whitespace correctly.
+	 */
+	len = ptr - state;
+	buf = palloc(len + 1);
+	memcpy(buf, state, len);
+	buf[len] = '\0';
+	scanint8(buf, false, &last_value);
+
+	/* Skip leading/trailing whitespace for parse_bool_with_len. */
+	ptr++;
+	while (isspace((unsigned char) *ptr))
+		ptr++;
+
+	len = strlen(ptr);
+	while (len > 0 && isspace((unsigned char) ptr[len - 1]))
+		len--;
+
+	/* Parse out the boolean */
+	if (!parse_bool_with_len(ptr, len, &is_called))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for local sequence state: \"%s\"",
+						state)));
+
+	pfree(buf);
+
+	seq = (FormLocalSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	seq->last_value = last_value;
+	seq->is_called = is_called;
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a403c64..147c571 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 825d8b2..2412a24 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index e859669..719a5f8 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -526,7 +526,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 622ccf7..fc0bd83 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,15 +14,21 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -35,19 +41,13 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -80,6 +80,14 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTupleData	tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -90,156 +98,216 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static Oid get_new_seqam_oid(Oid oldAM, char *accessMethod);
 
 
 /*
- * DefineSequence
- *				Creates a new sequence relation
+ * Build template column definition for a sequence relation.
  */
-Oid
-DefineSequence(CreateSeqStmt *seq)
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_from_type = false;
+	coldef->storage = 0;
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Create the relation for sequence.
+ */
+static Oid
+DefineSequenceRelation(RangeVar *rel, List *cols,
+					   bool if_not_exists, Oid ownerId)
 {
-	FormData_pg_sequence new;
-	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
-	Oid			seqoid;
-	Relation	rel;
-	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
 
-	/* Unlogged sequences are not implemented -- not clear if useful. */
-	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("unlogged sequences are not supported")));
+	stmt->relation = rel;
+	stmt->tableElts = cols;
+	stmt->inhRelations = NIL;
+	stmt->constraints = NIL;
+	stmt->options = NIL;
+	stmt->oncommit = ONCOMMIT_NOOP;
+	stmt->tablespacename = NULL;
+	stmt->if_not_exists = if_not_exists;
 
-	/*
-	 * If if_not_exists was given and a relation with the same name already
-	 * exists, bail out. (Note: we needn't check this when not if_not_exists,
-	 * because DefineRelation will complain anyway.)
-	 */
-	if (seq->if_not_exists)
-	{
-		RangeVarGetAndCheckCreationNamespace(seq->sequence, NoLock, &seqoid);
-		if (OidIsValid(seqoid))
-		{
-			ereport(NOTICE,
-					(errcode(ERRCODE_DUPLICATE_TABLE),
-					 errmsg("relation \"%s\" already exists, skipping",
-							seq->sequence->relname)));
-			return InvalidOid;
-		}
-	}
+	return DefineRelation(stmt, RELKIND_SEQUENCE, ownerId);
+}
 
-	/* Check and set all option values */
-	init_params(seq->options, true, &new, &owned_by);
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(int namcols, Oid *amcoltypes, char **amcolnames)
+{
+	List	   *seqcols;
+	int			colid;
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
 	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
+		ColumnDef  *coldef = makeSeqColumnDef();
 
-		coldef->inhcount = 0;
-		coldef->is_local = true;
 		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
+
+		switch (colid)
 		{
 			case SEQ_COL_NAME:
 				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
 				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
 				break;
 			case SEQ_COL_STARTVAL:
 				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
 				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
 				break;
 			case SEQ_COL_INCBY:
 				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
 				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
 				break;
 			case SEQ_COL_MAXVALUE:
 				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
 				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
 				break;
 			case SEQ_COL_MINVALUE:
 				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
 				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
 				break;
 			case SEQ_COL_CACHE:
 				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
 				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
 				break;
 			case SEQ_COL_CYCLE:
 				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
 				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
 				break;
 		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
+
+		seqcols = lappend(seqcols, coldef);
 	}
 
-	stmt->relation = seq->sequence;
-	stmt->inhRelations = NIL;
-	stmt->constraints = NIL;
-	stmt->options = NIL;
-	stmt->oncommit = ONCOMMIT_NOOP;
-	stmt->tablespacename = NULL;
-	stmt->if_not_exists = seq->if_not_exists;
+	for (colid = 0; colid < namcols; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		coldef->typeName = makeTypeNameFromOid(amcoltypes[colid], -1);
+		coldef->colname = amcolnames[colid];
+		coldef->is_not_null = true;
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	return seqcols;
+}
+
+
+/*
+ * DefineSequence
+ *				Creates a new sequence relation
+ */
+Oid
+DefineSequence(CreateSeqStmt *seq)
+{
+	FormData_pg_sequence new;
+	List	   *owned_by;
+	Oid			seqoid;
+	Oid			seqamid;
+	Relation	rel;
+	HeapTuple	tuple;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	NameData	name;
+	Oid		   *amcoltypes;
+	char	  **amcolnames;
+	int			namcols;
+	List	   *seqcols;
 
-	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
+	/* Unlogged sequences are not implemented -- not clear if useful. */
+	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("unlogged sequences are not supported")));
+
+	/*
+	 * If if_not_exists was given and a relation with the same name already
+	 * exists, bail out. (Note: we needn't check this when not if_not_exists,
+	 * because DefineRelation will complain anyway.)
+	 */
+	if (seq->if_not_exists)
+	{
+		RangeVarGetAndCheckCreationNamespace(seq->sequence, NoLock, &seqoid);
+		if (OidIsValid(seqoid))
+		{
+			ereport(NOTICE,
+					(errcode(ERRCODE_DUPLICATE_TABLE),
+					 errmsg("relation \"%s\" already exists, skipping",
+							seq->sequence->relname)));
+			return InvalidOid;
+		}
+	}
+
+	/* Check and set all param values */
+	init_params(seq->options, true, &new, &owned_by);
+
+	/* Build column definitions. */
+	seqamid = get_new_seqam_oid(InvalidOid, seq->accessMethod);
+	namcols = seqam_extra_columns(seqamid, &amcoltypes, &amcolnames);
+
+	seqcols = BuildSeqColumnDefList(namcols, amcoltypes, amcolnames);
+
+	/*
+	 * Fill values[] and nulls[] for the tuple.
+	 */
+	values = palloc0(list_length(seqcols) * sizeof(Datum));
+	nulls = palloc0(list_length(seqcols) * sizeof(bool));
+
+	namestrcpy(&name, seq->sequence->relname);
+	values[SEQ_COL_NAME - 1] = NameGetDatum(&name);
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new.start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new.increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new.max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new.min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new.cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new.is_cycled);
+
+	/* Let AM fill the values[] and nulls[] for the tuple as well. */
+	seqam_init(seqamid, InvalidOid, seq->options, seq->amoptions,
+			   values, nulls);
+
+	/* Create the sequence relation */
+	seqoid = DefineSequenceRelation(seq->sequence, seqcols,
+									seq->if_not_exists, seq->ownerId);
 	Assert(seqoid != InvalidOid);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
 	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	tupDesc = RelationGetDescr(rel);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -264,58 +332,70 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	HeapTuple	newtup;
+	Relation	seqrel;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle seqh;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
-	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	seqrel = seqh.rel;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	memset(replaces, true, tupDesc->natts * sizeof(bool));
+
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+
+	seqam_init(seqrel->rd_rel->relam, seqrelid,
+			   list_make1(makeDefElem("restart", NULL)), NULL,
+			   values, nulls);
+
+	newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqh.rel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqh.rel, newtup);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
 }
 
 /*
@@ -368,23 +448,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,18 +463,26 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 Oid
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
 	SeqTable	elm;
+	HeapTuple	tuple;
+	HeapTuple	newtup;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	List	   *seqoptions;
+	SequenceHandle seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -418,66 +490,158 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidOid;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	seqoptions = stmt->options;
+	init_params(seqoptions, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+	oldamid = seqrel->rd_rel->relam;
+	seqamid = get_new_seqam_oid(seqrel->rd_rel->relam, stmt->accessMethod);
 
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
+	/*
+	 * If we are changing sequence AM, we need to alter
+	 * the sequence relation.
+	 */
+	if (seqamid != oldamid)
 	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		bool	found;
+		Oid	   *oldamcoltypes,
+			   *newamcoltypes;
+		char  **oldamcolnames,
+			  **newamcolnames;
+		int		noldamcols,
+				nnewamcols;
+		int		i;
+		List   *atcmds = NIL;
+
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 *
+		 * This may not be the most beautiful way to do it but since
+		 * sequence AMs are expected to handle the RESTART option anyway,
+		 * it does not seem neccessary to invent special parameter for the
+		 * init API just for this.
+		 */
+		(void) sequence_get_restart_value(seqoptions, 0, &found);
+		if (!found)
+		{
+			DefElem	   *defel;
+			int64		last,
+						restart_value;
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+			restart_value = seqam_alloc(seqrel, &seqh, 1, &last);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+			defel = makeDefElem("restart", (Node *)makeInteger(restart_value));
+			seqoptions = lcons(defel, seqoptions);
+		}
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/*
+		 * Create a new storage file for the sequence.
+		 * See ResetSequence for more info.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
 
-		PageSetLSN(page, recptr);
-	}
+		/* Build DROP commands for columns of old AM. */
+		noldamcols = seqam_extra_columns(oldamid, &oldamcoltypes,
+										 &oldamcolnames);
+		for (i = 0; i < noldamcols; i++)
+		{
+			AlterTableCmd *cmd = makeNode(AlterTableCmd);
 
-	END_CRIT_SECTION();
+			cmd->subtype = AT_DropColumnFromSequence;
+			cmd->name = oldamcolnames[i];
+			cmd->behavior = DROP_RESTRICT;
+			cmd->missing_ok = TRUE;
+			atcmds = lappend(atcmds, cmd);
+		}
 
-	UnlockReleaseBuffer(buf);
+		/* Build ADD commands for columns of new AM. */
+		nnewamcols = seqam_extra_columns(seqamid, &newamcoltypes,
+										 &newamcolnames);
+		for (i = 0; i < nnewamcols; i++)
+		{
+			ColumnDef  *coldef = makeSeqColumnDef();
+			AlterTableCmd *cmd = makeNode(AlterTableCmd);
+
+			coldef->typeName = makeTypeNameFromOid(newamcoltypes[i], -1);
+			coldef->colname = newamcolnames[i];
+			coldef->is_not_null = true;
+
+			cmd->subtype = AT_AddColumnToSequence;
+			cmd->def = (Node *) coldef;
+			atcmds = lappend(atcmds, cmd);
+		}
+
+		/* Run the commands to add/remove columns. */
+		AlterTableInternal(seqrelid, atcmds, false);
+
+		tupDesc = RelationGetDescr(seqrel);
+
+		values = (Datum *) repalloc(values, tupDesc->natts * sizeof(Datum));
+		nulls = (bool *) repalloc(nulls, tupDesc->natts * sizeof(bool));
+
+		/* Let the new sequence AM initialize. */
+		seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+				   values, nulls);
+
+		/* Rebuild the sequence tuple and save it. */
+		newtup = heap_form_tuple(tupDesc, values, nulls);
+		fill_seq_with_data(seqh.rel, newtup);
+
+		/* Update the catalog. */
+		seqrel_update_relam(seqrelid, seqamid);
+	}
+	else
+	{
+		/* Let sequence AM update the tuple. */
+		replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+		memset(replaces, true, tupDesc->natts * sizeof(bool));
+		seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+				   values, nulls);
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
+		sequence_save_tuple(&seqh, newtup, true);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	elm->cached = elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
-	return relid;
+	return seqrelid;
 }
 
 
@@ -516,29 +680,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -556,121 +715,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -679,91 +732,38 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return result;
 }
 
+
 Datum
 currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -804,31 +804,24 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -838,102 +831,82 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	if (!seqrel->rd_islocaltemp)
 		PreventCommandIfReadOnly("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
+	seqam_setval(seqrel, &seqh, next);
 
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
-
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	last_used_seq = elm;
 
-	END_CRIT_SECTION();
+	sequence_close(&seqh);
 
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	char	   *state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
 
-	do_setval(relid, next, true);
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
+
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Convert the data into 'local' sequence dump format and call restore API. */
+	state = seqam_local_format_state(next, iscalled);
+	seqam_restore_state(seqh.rel, &seqh, state);
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -992,21 +965,20 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
 {
-	SeqTable	elm;
-	Relation	seqrel;
-	bool		found;
+	SeqTable		elm;
+	Relation		seqrel;
+	bool			found;
 
 	/* Find or create a hash table entry for this sequence */
 	if (seqhashtab == NULL)
 		create_seq_hashtable();
 
-	elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);
+	elm = (SeqTable) hash_search(seqhashtab, &seqrelid, HASH_ENTER, &found);
 
 	/*
 	 * Initialize the new hash table entry if it did not exist already.
@@ -1048,44 +1020,57 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	sequence_release_tuple(seqh);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
+	Form_pg_sequence seq_form;
+
+	if (seqh->tup.t_data != NULL)
+	{
+		return &seqh->tup;
+	}
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of seqh->tup */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1095,33 +1080,139 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
 	}
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	seq_form = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
 
 	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	seqh->elm->increment = seq_form->increment_by;
+
+	return &seqh->tup;
+}
+
+/*
+ * Update the page, optionally do WAL logging of the tuple
+ */
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal)
+{
+	Page	page;
+
+	Assert(seqh->tup.t_data != NULL);
+
+	page = BufferGetPage(seqh->buf);
+
+	if (HeapTupleIsValid(newtup))
+	{
+		/*
+		 * New tuple was passed, we must process it and replace the old one on
+		 * the same page.
+		 */
+		Page	temppage;
+
+		/* Sequence tuples are always frozen. */
+		HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+		HeapTupleHeaderSetXminFrozen(newtup->t_data);
+		HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+		HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+		newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+		ItemPointerSet(&newtup->t_data->t_ctid, 0, FirstOffsetNumber);
+
+		/*
+		 * Replace the original tuple on the page.
+		 */
+		temppage = PageGetTempPageCopySpecial(page);
+
+		if (PageAddItem(temppage, (Item) newtup->t_data, newtup->t_len,
+					FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+			elog(PANIC, "sequence_save_tuple: failed to add item to page");
+
+		PageSetLSN(temppage, PageGetLSN(page));
+
+		START_CRIT_SECTION();
+
+		PageRestoreTempPage(temppage, page);
+		seqh->tup.t_data = newtup->t_data;
+		seqh->tup.t_len = newtup->t_len;
+		MarkBufferDirty(seqh->buf);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, newtup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+	else
+	{
+		/*
+		 * New tuple was not sent, so the original tuple was probably just
+		 * changed inline, all we need to do is mark the buffer dirty and
+		 * optionally log the update tuple.
+		 */
+		START_CRIT_SECTION();
+
+		MarkBufferDirty(seqh->buf);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+}
+
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
+	{
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
+	}
+}
+
+/*
+ * Returns true if sequence was not WAL logged since checkpoint
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr = GetRedoRecPtr();
 
-	return seq;
+	Assert(BufferIsValid(seqh->buf));
+
+	page = BufferGetPage(seqh->buf);
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
+ *
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1131,13 +1222,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1208,13 +1299,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1223,7 +1307,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1233,7 +1316,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1242,7 +1324,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1250,14 +1331,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1265,7 +1344,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1319,48 +1397,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1375,14 +1411,13 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1398,8 +1433,7 @@ process_owned_by(Relation seqrel, List *owned_by)
 
 	nnames = list_length(owned_by);
 	Assert(nnames > 0);
-	if (nnames == 1)
-	{
+	if (nnames == 1)	{
 		/* Must be OWNED BY NONE */
 		if (strcmp(strVal(linitial(owned_by)), "none") != 0)
 			ereport(ERROR,
@@ -1486,20 +1520,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1517,7 +1548,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1525,12 +1556,99 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_dump_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *result;
+	SequenceHandle seqh;
+
+	sequence_open(relid, &seqh);
+
+	result = seqam_dump_state(seqh.rel, &seqh);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_CSTRING(result);
+}
+
+Datum
+pg_sequence_restore_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *state = PG_GETARG_CSTRING(1);
+	SequenceHandle seqh;
+
+	sequence_open(relid, &seqh);
+
+	seqam_restore_state(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+		CatalogUpdateIndexes(rd, ctup);
+	}
+
+	heap_freetuple(ctup);
+	heap_close(rd, RowExclusiveLock);
+	CommandCounterIncrement();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1582,6 +1700,7 @@ seq_redo(XLogReaderState *record)
 	pfree(localpage);
 }
 
+
 /*
  * Flush cached sequence information.
  */
@@ -1596,3 +1715,161 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+static Oid
+get_new_seqam_oid(Oid oldAM, char *accessMethod)
+{
+
+	if (oldAM && accessMethod == NULL)
+		return oldAM;
+	else if (accessMethod == NULL)
+		return get_seqam_oid(default_seqam, false);
+	else
+		return get_seqam_oid(accessMethod, false);
+}
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64 next = *value;
+	int64 rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
+						bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
+					 bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 66d5083..d47bdd5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -265,6 +265,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -2834,6 +2835,8 @@ AlterTableGetLockLevel(List *cmds)
 				 */
 			case AT_DropColumn:	/* change visible to SELECT */
 			case AT_AddColumnToView:	/* CREATE VIEW */
+			case AT_AddColumnToSequence:	/* ALTER SEQUENCE USING */
+			case AT_DropColumnFromSequence:	/* ALTER SEQUENCE USING */
 			case AT_DropOids:	/* calls AT_DropColumn */
 			case AT_EnableAlwaysRule:	/* may change SELECT rules */
 			case AT_EnableReplicaRule:	/* may change SELECT rules */
@@ -3093,6 +3096,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			/* Recursion occurs during execution phase */
 			pass = AT_PASS_ADD_COL;
 			break;
+		case AT_AddColumnToSequence:	/* add column via ALTER SEQUENCE
+										   USING */
+			ATSimplePermissions(rel, ATT_SEQUENCE);
+			ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
+			/* This command never recurses */
+			pass = AT_PASS_MISC;
+			break;
 		case AT_ColumnDefault:	/* ALTER COLUMN DEFAULT */
 
 			/*
@@ -3143,6 +3153,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			/* Recursion occurs during execution phase */
 			pass = AT_PASS_DROP;
 			break;
+		case AT_DropColumnFromSequence:	/* drop column via ALTER SEQUENCE
+										   USING */
+			ATSimplePermissions(rel, ATT_SEQUENCE);
+			ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
+			/* This command never recurses */
+			pass = AT_PASS_MISC;
+			break;
 		case AT_AddIndex:		/* ADD INDEX */
 			ATSimplePermissions(rel, ATT_TABLE);
 			/* This command never recurses */
@@ -3395,6 +3412,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		case AT_AddColumn:		/* ADD COLUMN */
 		case AT_AddColumnToView:		/* add column via CREATE OR REPLACE
 										 * VIEW */
+		case AT_AddColumnToSequence:	/* add column via ALTER SEQUENCE
+										   USING */
 			ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
 							false, false, false, lockmode);
 			break;
@@ -3424,6 +3443,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
 			break;
 		case AT_DropColumn:		/* DROP COLUMN */
+		case AT_DropColumnFromSequence:		/* ALTER SEQUENCE USING */
 			ATExecDropColumn(wqueue, rel, cmd->name,
 					 cmd->behavior, false, false, cmd->missing_ok, lockmode);
 			break;
@@ -4207,6 +4227,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4271,6 +4294,9 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a foreign table");
 			break;
+		case ATT_SEQUENCE:
+			msg = _("\"%s\" is not a sequence");
+			break;
 		default:
 			/* shouldn't get here, add all necessary cases above */
 			msg = _("\"%s\" is of the wrong type");
@@ -9040,7 +9066,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f1a24f5..ffde815 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3362,7 +3362,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3375,7 +3377,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6e8b308..2d731d6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1568,7 +1568,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1579,7 +1581,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 36dac29..8d6a402 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3531,7 +3532,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3553,7 +3580,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3561,11 +3612,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3624,7 +3698,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 3ccdbb7..64f75b3 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -399,6 +399,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 2e4d0b3..a648a30 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 24c92e7..a324552 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1055,11 +1057,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1534,6 +1539,39 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for an index relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	MemoryContext	indexcxt;
+	Form_pg_seqam	amform;
+
+	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+									 RelationGetRelationName(rel),
+									 ALLOCSET_SMALL_MINSIZE,
+									 ALLOCSET_SMALL_INITSIZE,
+									 ALLOCSET_SMALL_MAXSIZE);
+	rel->rd_indexcxt = indexcxt;
+
+	rel->rd_aminfo = (RelationAmInfo *)
+		MemoryContextAllocZero(rel->rd_indexcxt,
+							   sizeof(RelationAmInfo));
+
+	/*
+	 * Make a copy of the pg_am entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+}
 
 /*
  *		formrdesc
@@ -4806,6 +4844,22 @@ load_relcache_init_file(bool shared)
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
+		else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+		{
+			MemoryContext indexcxt;
+			Assert(!rel->rd_isnailed);
+			Assert(false);
+
+			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+											 RelationGetRelationName(rel),
+											 ALLOCSET_SMALL_MINSIZE,
+											 ALLOCSET_SMALL_INITSIZE,
+											 ALLOCSET_SMALL_MAXSIZE);
+			rel->rd_indexcxt = indexcxt;
+			/* set up zeroed fmgr-info vectors */
+			rel->rd_aminfo = (RelationAmInfo *)
+				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+		}
 		else
 		{
 			/* Count nailed rels to ensure we have 'em all */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index bd27168..5341a09 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAMNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_seqamname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAMOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f6df077..3431717 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2788,6 +2789,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"default_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence access method for any new sequences."),
+			gettext_noop("Defaults to 'local' sequence access method."),
+			GUC_IS_NAME
+		},
+		&default_seqam,
+		"local",
+		check_default_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index b053659..7b26bb5 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -508,6 +508,7 @@
 #default_tablespace = ''		# a tablespace name, '' uses the default
 #temp_tablespaces = ''			# a list of tablespace names, '' uses
 					# only default tablespace
+#default_sequenceam = 'local'	# default sequence access method
 #check_function_bodies = on
 #default_transaction_isolation = 'read committed'
 #default_transaction_read_only = off
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 1e330f2..251f8cd 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14590,7 +14590,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = "local";
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14660,15 +14661,41 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	}
 #endif
 
-	startv = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
+	startv = pg_strdup(PQgetvalue(res, 0, 1));
+	incby = pg_strdup(PQgetvalue(res, 0, 2));
 	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
+		maxv = pg_strdup(PQgetvalue(res, 0, 3));
 	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
+		minv = pg_strdup(PQgetvalue(res, 0, 4));
+	cache = pg_strdup(PQgetvalue(res, 0, 5));
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
+	PQclear(res);
+
+	res = ExecuteSqlQuery(fout, "SELECT EXISTS(SELECT 1 "
+								"FROM pg_catalog.pg_class c, "
+								"pg_catalog.pg_namespace n "
+								"WHERE n.oid = c.relnamespace "
+								"AND nspname = 'pg_catalog'"
+								"AND c.relname = 'pg_seqam' "
+								"AND c.relkind = 'r');",
+						  PGRES_TUPLES_OK);
+	if (strcmp(PQgetvalue(res, 0, 0), "t") == 0)
+	{
+		PQclear(res);
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		amname = pg_strdup(PQgetvalue(res, 0, 0));
+	}
+
+	PQclear(res);
+
 	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
@@ -14710,6 +14737,7 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14776,8 +14804,6 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
-	PQclear(res);
-
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(delqry);
 	destroyPQExpBuffer(labelq);
@@ -14792,17 +14818,30 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
+	char	   *state;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	res = ExecuteSqlQuery(fout, "SELECT EXISTS(SELECT 1 "
+								"FROM pg_catalog.pg_proc p, "
+								"pg_catalog.pg_namespace n "
+								"WHERE n.oid = p.pronamespace "
+								"AND nspname = 'pg_catalog'"
+								"AND p.proname = 'pg_sequence_dump_state');",
+						  PGRES_TUPLES_OK);
+
+	if (strcmp(PQgetvalue(res, 0, 0), "t") == 0)
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(last_value::text || ', ' || is_called::text) FROM %s",
+						  fmtId(tbinfo->dobj.name));
+	else
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_dump_state(%s))",
+						  fmtId(tbinfo->dobj.name));
 
+	PQclear(res);
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
 	if (PQntuples(res) != 1)
@@ -14814,14 +14853,12 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+	state = PQgetvalue(res, 0, 0);
 
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+	appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_restore_state(");
 	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+	appendPQExpBuffer(query, ", %s);\n", state);
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4cda07d..45a4614 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1395,30 +1395,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1462,6 +1438,14 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S' && !verbose)
+		appendPQExpBufferStr(&buf, " AND attname IN ('sequence_name', "
+							 "'start_value', 'increment_by', 'max_value', "
+							 "'min_value', 'cache_value', 'is_cycled')");
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
 	res = PSQLexec(buf.data);
@@ -1469,6 +1453,39 @@ describeOneTableDetails(const char *schemaname,
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1780,6 +1797,29 @@ describeOneTableDetails(const char *schemaname,
 		/* Footer information about a sequence */
 		PGresult   *result = NULL;
 
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
+		/*
+		 * If we get no rows back, don't show anything (obviously). We should
+		 * never get more than one row back, but if we do, just ignore it and
+		 * don't print anything.
+		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
+		PQclear(result);
+
 		/* Get the column that owns this sequence */
 		printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
 						  "\n   pg_catalog.quote_ident(relname) || '.' ||"
@@ -1797,6 +1837,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1806,11 +1848,6 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
-		/*
-		 * If we get no rows back, don't show anything (obviously). We should
-		 * never get more than one row back, but if we do, just ignore it and
-		 * don't print anything.
-		 */
 		PQclear(result);
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index e7b6bb5..4c28c8c 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -46,8 +46,9 @@ typedef enum relopt_kind
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
 	RELOPT_KIND_BRIN = (1 << 10),
+	RELOPT_KIND_SEQUENCE = (1 << 11),
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -270,8 +271,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
-				 bool validate);
+extern bytea *am_reloptions(RegProcedure amoptions, Datum reloptions,
+						   bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..0a437ef
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,68 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+extern char *default_seqam;
+
+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);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern char *seqam_dump_state(Relation seqrel, SequenceHandle *seqh);
+extern void seqam_restore_state(Relation seqrel, SequenceHandle *seqh,
+								char *state);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup,
+								bool do_wal);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_dump(PG_FUNCTION_ARGS);
+extern Datum seqam_local_restore(PG_FUNCTION_ARGS);
+extern char *seqam_local_format_state(int64 value, bool iscalled);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index a680229..09df8c3 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAMNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAMOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9edfdb8..401250e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1819,6 +1819,10 @@ 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_ ));
+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_ ));
+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_ ));
 DESCR("I/O");
@@ -5080,6 +5084,20 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "26 2281 17 2281 2281" _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_dump	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 2275 "2281 2281" _null_ _null_ _null_ _null_ seqam_local_dump _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_restore	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 2275" _null_ _null_ _null_ _null_ seqam_local_restore _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..d35f679
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	32
+
+CATALOG(pg_seqam,32)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamdump;			/* dump state, used by pg_dump */
+	regproc		seqamrestore;		/* restore state, used when loading pg_dump */
+#ifdef CATALOG_VARLEN
+	Oid			seqamcoltypes[1];	/* extra column types */
+	text		seqamcolnames[1];   /* extra column names (must be same length as seqamcoltypes */
+#endif
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						9
+#define Anum_pg_seqam_seqamname				1
+#define Anum_pg_seqam_seqamreloptions		2
+#define Anum_pg_seqam_seqaminit				3
+#define Anum_pg_seqam_seqamalloc			4
+#define Anum_pg_seqam_seqamsetval			5
+#define Anum_pg_seqam_seqamdump				6
+#define Anum_pg_seqam_seqamrestore			7
+#define Anum_pg_seqam_seqamcoltypes			8
+#define Anum_pg_seqam_seqamcolnames			9
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 6022 (  local		seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_dump seqam_local_restore "{20,20,16}" "{last_value,log_cnt,is_called}"));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 1baf43d..6a92438 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -23,15 +23,12 @@
 typedef struct FormData_pg_sequence
 {
 	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,18 +38,14 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  */
 
 #define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
-
+#define SEQ_COL_STARTVAL		2
+#define SEQ_COL_INCBY			3
+#define SEQ_COL_MAXVALUE		4
+#define SEQ_COL_MINVALUE		5
+#define SEQ_COL_CACHE			6
+#define SEQ_COL_CYCLE			7
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_CYCLE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -71,6 +64,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_dump_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_restore_state(PG_FUNCTION_ARGS);
 
 extern Oid	DefineSequence(CreateSeqStmt *stmt);
 extern Oid	AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b1dfa85..2dd2f71 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1285,6 +1285,7 @@ typedef enum AlterTableType
 	AT_AddColumn,				/* add column */
 	AT_AddColumnRecurse,		/* internal to commands/tablecmds.c */
 	AT_AddColumnToView,			/* implicitly via CREATE OR REPLACE VIEW */
+	AT_AddColumnToSequence,		/* implicitly via ALTER SEQUENCE USING */
 	AT_ColumnDefault,			/* alter column default */
 	AT_DropNotNull,				/* alter column drop not null */
 	AT_SetNotNull,				/* alter column set not null */
@@ -1294,6 +1295,7 @@ typedef enum AlterTableType
 	AT_SetStorage,				/* alter column set storage */
 	AT_DropColumn,				/* drop column */
 	AT_DropColumnRecurse,		/* internal to commands/tablecmds.c */
+	AT_DropColumnFromSequence,	/* implicitly via ALTER SEQUENCE USING */
 	AT_AddIndex,				/* add index */
 	AT_ReAddIndex,				/* internal to commands/tablecmds.c */
 	AT_AddConstraint,			/* add constraint */
@@ -2013,8 +2015,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2022,8 +2026,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 717f46b..16078c8 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -404,6 +404,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_default_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 6bd786d..c080487 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -48,10 +49,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
+ * functions, defined by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+	/* pg_am only */
 	FmgrInfo	aminsert;
 	FmgrInfo	ambeginscan;
 	FmgrInfo	amgettuple;
@@ -61,6 +64,14 @@ typedef struct RelationAmInfo
 	FmgrInfo	ammarkpos;
 	FmgrInfo	amrestrpos;
 	FmgrInfo	amcanreturn;
+	FmgrInfo	amcostestimate;
+
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+
+	/* Common */
+	FmgrInfo	amoptions;
 } RelationAmInfo;
 
 
@@ -131,23 +142,25 @@ typedef struct RelationData
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+
 	/*
-	 * index access support info (used only for an index relation)
+	 * Access support info (used only for index or sequence relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
 	 * arrays are indexed by support function number, which is a sufficient
 	 * identifier given that restriction.
 	 *
-	 * Note: rd_amcache is available for index AMs to cache private data about
-	 * an index.  This must be just a cache since it may get reset at any time
+	 * Note: rd_amcache is available for AMs to cache private data about
+	 * an object.  This must be just a cache since it may get reset at any time
 	 * (in particular, it will get reset by a relcache inval message for the
 	 * index).  If used, it must point to a single memory chunk palloc'd in
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
-	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am */
+	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am or pg_seqam */
 	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
@@ -158,7 +171,7 @@ typedef struct RelationData
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
-	void	   *rd_amcache;		/* available for use by index AM */
+	void	   *rd_amcache;		/* available for use by AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
 
 	/*
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index ba0b090..ac04502 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index c7be273..d4e250f 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..ea45a2d 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_restore_state('sequence_test'::text, '99, false');
+ pg_sequence_restore_state 
+---------------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_restore_state('sequence_test'::regclass, '99, false');
+ pg_sequence_restore_state 
+---------------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | last_value | log_cnt | is_called 
+---------------+-------------+--------------+---------------------+-----------+-------------+-----------+------------+---------+-----------
+ foo_seq       |           1 |            1 | 9223372036854775807 |         1 |           1 | f         |          1 |       0 | f
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | last_value | log_cnt | is_called 
+---------------+-------------+--------------+---------------------+-----------+-------------+-----------+------------+---------+-----------
+ foo_seq       |           1 |            1 | 9223372036854775807 |         1 |           1 | f         |          2 |      31 | t
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 80c5706..d1bc3d7 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -100,14 +100,14 @@ SELECT table_name, column_name, is_updatable
  ro_view17  | b             | NO
  ro_view18  | a             | NO
  ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
  ro_view19  | start_value   | NO
  ro_view19  | increment_by  | NO
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
+ ro_view19  | last_value    | NO
+ ro_view19  | log_cnt       | NO
  ro_view19  | is_called     | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..aa60bfd 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_restore_state('sequence_test'::text, '99, false');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_restore_state('sequence_test'::regclass, '99, false');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
-- 
1.9.1

0002-seqam-ddl-v2.patchtext/x-diff; name=0002-seqam-ddl-v2.patchDownload
>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

0003-gapless-sequence-v3.patchtext/x-diff; name=0003-gapless-sequence-v3.patchDownload
>From 596beb1f5c76123b2cefa77d4dcbde5034fe58bf Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Tue, 9 Dec 2014 22:57:53 +0100
Subject: [PATCH 3/3] gapless sequence v3

---
 contrib/gapless_seq/Makefile                 |  65 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  71 ++++
 contrib/gapless_seq/gapless_seq.c            | 485 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/gapless_seq.sgml         |  22 ++
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 ++++
 9 files changed, 915 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/gapless_seq.sgml
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql

diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..63b51c4
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,65 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-install=./tmp_check \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq
+	$(pg_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq
+	$(pg_isolation_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..a3641e1
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_dump_state('test_gapless');
+ pg_sequence_dump_state 
+------------------------
+ 2, t
+(1 row)
+
+SELECT pg_sequence_restore_state('test_gapless', pg_sequence_dump_state('test_gapless'));
+ pg_sequence_restore_state 
+---------------------------
+ 
+(1 row)
+
+SELECT pg_sequence_dump_state('test_gapless');
+ pg_sequence_dump_state 
+------------------------
+ 2, t
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+ERROR:  cannot drop sequence access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on sequence access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..dd3afcd
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,71 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_init(OID, INTERNAL, BYTEA, INTERNAL, INTERNAL)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_alloc(INTERNAL, INTERNAL, BIGINT, INTERNAL)
+RETURNS BIGINT
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_setval(INTERNAL, INTERNAL, BIGINT)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_dump(INTERNAL, INTERNAL)
+RETURNS CSTRING
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_restore(INTERNAL, INTERNAL, CSTRING)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	is_called bool NOT NULL,
+	last_value bigint NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relam = (
+			SELECT oid FROM pg_seqam WHERE seqamname = 'gapless'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD FOR SEQUENCES gapless (tx_start_value bigint, tx_last_value bigint, xid xid) AS (
+	reloptions = seqam_local_reloptions,	/* reloptions parser is same as local (no reloptions) */
+	init = seqam_gapless_init,				/* init new gapless sequence */
+	alloc = seqam_gapless_alloc,			/* logs and returns each value... slow */
+	setval = seqam_gapless_setval,			/* setval support */
+	dump = seqam_gapless_dump,				/* pgdump support */
+	restore = seqam_gapless_restore			/* pgdump support */
+);
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..e2e42c8
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,485 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = Gapless functions
+ *
+ *------------------------------------------------------------
+ */
+extern Datum seqam_gapless_init(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_dump(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_restore(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_init);
+PG_FUNCTION_INFO_V1(seqam_gapless_alloc);
+PG_FUNCTION_INFO_V1(seqam_gapless_setval);
+PG_FUNCTION_INFO_V1(seqam_gapless_dump);
+PG_FUNCTION_INFO_V1(seqam_gapless_restore);
+
+typedef struct FormGaplessSequence
+{
+	FormData_pg_sequence seq;
+	uint32		xid;
+} FormGaplessSequence;
+
+typedef struct FormGaplessValue
+{
+	Oid		seqid;
+	bool	is_called;
+	int64	last_value;
+} FormGaplessValue;
+
+#define SEQAM_GAPLESS_COL_XID SEQ_COL_LASTCOL + 1
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static FormGaplessSequence *wait_for_sequence(SequenceHandle *seqh,
+										      TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, int64 last_value,
+							   bool is_called, HeapTuple oldtuple);
+
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ * TODO: more checks
+ */
+Datum
+seqam_gapless_init(PG_FUNCTION_ARGS)
+{
+	Oid		seqrelid = PG_GETARG_OID(0);
+	List   *seqoptions = (List *) PG_GETARG_POINTER(1);
+	Datum  *values = (Datum *) PG_GETARG_POINTER(3);
+	bool   *nulls = (bool *) PG_GETARG_POINTER(4);
+	bool	found_restart;
+	int64	start_value,
+			last_value,
+			min_value,
+			max_value;
+	Relation		valrel;
+	HeapTuple		tuple = NULL;
+	TransactionId	local_xid = GetTopTransactionId();
+
+	/* Get the new value to use as starting point. */
+	start_value = DatumGetInt64(values[SEQ_COL_STARTVAL - 1]);
+	start_value = sequence_get_restart_value(seqoptions, start_value,
+											 &found_restart);
+
+	/* Load current value if this is existing sequence. */
+	if (seqrelid != InvalidOid)
+	{
+		valrel = open_values_rel();
+		tuple = get_last_value_tup(valrel, seqrelid);
+	}
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we use the starting value
+	 * otherwise we use the stored value.
+	 */
+	if (seqrelid == InvalidOid || found_restart || !HeapTupleIsValid(tuple))
+		last_value = start_value;
+	else
+	{
+		FormGaplessValue *v = (FormGaplessValue *) GETSTRUCT(tuple);
+		last_value = v->last_value;
+	}
+
+	/* Validate the min/max against the starting point. */
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+	sequence_check_range(last_value, min_value, max_value);
+
+	/*
+	 * If this is existing sequence with new RESTART value we should update
+	 * our state to that value.
+	 */
+	if (seqrelid != InvalidOid && found_restart)
+		set_last_value_tup(valrel, seqrelid, last_value, false, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	if (seqrelid != InvalidOid)
+		heap_close(valrel, NoLock);
+
+	/* Update the xid info */
+	values[SEQAM_GAPLESS_COL_XID -1] = UInt32GetDatum(local_xid);
+	nulls[SEQAM_GAPLESS_COL_XID -1] = false;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+Datum
+seqam_gapless_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	/* we ignore nreguested as gapless sequence can't do caching */
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	FormGaplessSequence *seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		FormGaplessValue *v = (FormGaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->seq.min_value,
+									  seq->seq.max_value,
+									  seq->seq.increment_by,
+									  seq->seq.is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->seq.start_value;
+
+	/*
+	 * Insert or update the last value tuple.
+	 */
+	set_last_value_tup(valrel, seqrelid, result, true, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/*
+	 * If current tx is different fron the last one,
+	 * update the sequence tuple as well.
+	 *
+	 * We don't need to WAL log the update as the only thing we save to
+	 * sequence tuple is the active transaction id and we know that in case of
+	 * crash the transaction id will not be active so it's ok to lose the
+	 * update.
+	 */
+	if (seq->xid != local_xid)
+	{
+		seq->xid = local_xid;
+		sequence_save_tuple(seqh, NULL, false);
+	}
+
+	*last = result;
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+Datum
+seqam_gapless_setval(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_dump()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ *
+ * Format is: '<last_value::bigint>, <is_called>::bool'
+ */
+Datum
+seqam_gapless_dump(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	/* int64 + colon + space + t/f + null byte */
+#define SEQAM_GAPLESS_STATE_LEN (19 + 1 + 1 + 1 + 1)
+	char	   *state = (char *) palloc0(SEQAM_GAPLESS_STATE_LEN);
+	Relation	valrel;
+	HeapTuple	tuple;
+	int64		last_value;
+	bool		is_called;
+
+	/*
+	 * Get the last value from the values table, if not found use start_value
+	 * from the sequence definition.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		FormGaplessValue *v = (FormGaplessValue *) GETSTRUCT(tuple);
+		last_value = v->last_value;
+		is_called = v->is_called;
+	}
+	else
+	{
+		FormGaplessSequence *seq = (FormGaplessSequence *)
+			GETSTRUCT(sequence_read_tuple(seqh));
+		last_value = seq->seq.start_value;
+		is_called = false;
+	}
+
+	snprintf(state, SEQAM_GAPLESS_STATE_LEN, INT64_FORMAT", %s",
+			 last_value, is_called ? "t" : "f");
+
+	PG_RETURN_CSTRING(state);
+}
+
+/*
+ * seqam_gapless_restore()
+ *
+ * Restore previously dumpred state of gapless sequence
+ *
+ * Format is: '<last_value::bigint>, <is_called>::bool'
+ */
+Datum
+seqam_gapless_restore(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char	   *state = PG_GETARG_CSTRING(2);
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	int64		last_value;
+	bool		is_called;
+	char	   *buf;
+	char	   *ptr = state;
+	int			len;
+	FormGaplessSequence *seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Check that we can find comma. */
+	while (*ptr != ',' && *ptr != '\0')
+		ptr++;
+
+	if (*ptr == '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for gapless sequence state: \"%s\"",
+						state)));
+
+	/*
+	 * Copy the part of the string before comma into work buffer
+	 * for scanint8, it will handle leading/trailing whitespace correctly.
+	 */
+	len = ptr - state;
+	buf = palloc(len + 1);
+	memcpy(buf, state, len);
+	buf[len] = '\0';
+	scanint8(buf, false, &last_value);
+
+	/* Skip leading/trailing whitespace for parse_bool_with_len. */
+	ptr++;
+	while (isspace((unsigned char) *ptr))
+		ptr++;
+
+	len = strlen(ptr);
+	while (len > 0 && isspace((unsigned char) ptr[len - 1]))
+		len--;
+
+	/* Parse out the boolean */
+	if (!parse_bool_with_len(ptr, len, &is_called))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for for gapless sequence state: \"%s\"",
+						state)));
+
+	pfree(buf);
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * Insert or update the last value tuple.
+	 */
+	set_last_value_tup(valrel, seqrelid, last_value, is_called, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	seq->xid = local_xid;
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormGaplessSequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormGaplessSequence *seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (seq->xid != local_xid && TransactionIdIsInProgress(seq->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seq->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = (FormGaplessSequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, int64 last_value, bool is_called, HeapTuple oldtuple)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/gapless_seq.sgml b/contrib/gapless_seq/gapless_seq.sgml
new file mode 100644
index 0000000..3eb536f
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.sgml
@@ -0,0 +1,22 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless_seq" xreflabel="gapless_seq">
+ <title>gapless sequence</title>
+
+ <indexterm zone="gapless_seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence.
+  This could be very costly and is not recommended for general
+  usage except in specific applications that require this feature.
+ </para>
+
+</sect1>
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..dd5ee00
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_dump_state('test_gapless');
+SELECT pg_sequence_restore_state('test_gapless', pg_sequence_dump_state('test_gapless'));
+SELECT pg_sequence_dump_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
-- 
1.9.1

#62Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Petr Jelinek (#61)
Re: Sequence Access Method WIP

On 01/23/2015 02:34 AM, Petr Jelinek wrote:

On 22/01/15 17:02, Petr Jelinek wrote:

The new version (the one that is not submitted yet) of gapless sequence
is way more ugly and probably not best example either but does guarantee
gaplessness (it stores the last value in it's own value table). So I am
not sure if it should be included either...

Here it is as promised.

I generally like the division of labour between the seqam
implementations and the API now.

I don't like the default_sequenceam GUC. That seems likely to create
confusion. And it's not really enough for something like a remote
sequence AM anyway that definitely needs some extra reloptions, like the
hostname of the remote server. The default should be 'local', unless you
specify something else with CREATE SEQUENCE USING - no GUCs.

Some comments on pg_dump:

* In pg_dump's dumpSequence function, check the server version number
instead of checking whether pg_sequence_dump_state() function exists.
That's what we usually do when new features are introduced. And there's
actually a bug there: you have the check backwards. (try dumping a
database with any sequences in it; it fails)

* pg_dump should not output a USING clause when a sequence uses the
'local' sequence. No point in adding such noise - making the SQL command
not standard-compatible - for the 99% of databases that don't use other
sequence AMs.

* Be careful to escape all strings correctly in pg_dump. I think you're
missing escaping for the 'state' variable at least.

In sequence_save_tuple:

else
{
/*
* New tuple was not sent, so the original tuple was probably just
* changed inline, all we need to do is mark the buffer dirty and
* optionally log the update tuple.
*/
START_CRIT_SECTION();

MarkBufferDirty(seqh->buf);

if (do_wal)
log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);

END_CRIT_SECTION();
}

This is wrong when checksums are enabled and !do_wal. I believe this
should use MarkBufferDirtyHint().

Notable changes:
- The gapless sequence rewritten to use the transactional storage as
that's the only way to guarantee gaplessness between dump and restore.

Can you elaborate?

Using the auxiliary seqam_gapless_values is a bit problematic. First of
all, the event trigger on DROP SEQUENCE doesn't fire if you drop the
whole schema containing the sequence with DROP SCHEMA CASCADE. Secondly,
updating a row in a table for every nextval() call is pretty darn
expensive. But I don't actually see a problem with dump and restore.

Can you rewrite it without using the values table? AFAICS, there are a
two of problems to solve:

1. If the top-level transaction aborts, you need to restore the old
value. I suggest keeping two values in the sequence tuple: the old,
definitely committed one, and the last value. The last value is only
considered current if the associated XID committed; otherwise the old
value is current. When a transaction is about to commit, it stores its
top-level XID and the new value in the "last" field, and copies the
previously current value to the "old" field.

2. You need to track the last value on a per-subtransaction basis, until
the transaction commits/rolls back, in order to have "rollback to
savepoint" to retreat the sequence's value. That can be done in
backend-private memory, maintaining a stack of subtransactions and the
last value of each. Use RegisterSubXactCallback to hook into subxact
commit/abort. Just before top-level commit (in pre-commit callback),
update the sequence tuple with the latest value, so that it gets
WAL-logged before the commit record.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#63Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#62)
Re: Sequence Access Method WIP

On 28/01/15 18:09, Heikki Linnakangas wrote:

On 01/23/2015 02:34 AM, Petr Jelinek wrote:

On 22/01/15 17:02, Petr Jelinek wrote:

The new version (the one that is not submitted yet) of gapless sequence
is way more ugly and probably not best example either but does guarantee
gaplessness (it stores the last value in it's own value table). So I am
not sure if it should be included either...

Here it is as promised.

I generally like the division of labour between the seqam
implementations and the API now.

I don't like the default_sequenceam GUC. That seems likely to create
confusion. And it's not really enough for something like a remote
sequence AM anyway that definitely needs some extra reloptions, like the
hostname of the remote server. The default should be 'local', unless you
specify something else with CREATE SEQUENCE USING - no GUCs.

Hmm, I am not too happy about this, I want SERIAL to work with custom
sequences (as long as it's possible for the sequence to work that way at
least). That's actually important feature for me. Your argument about
this being potential problem for some sequenceAMs is valid though.

Some comments on pg_dump:

* In pg_dump's dumpSequence function, check the server version number
instead of checking whether pg_sequence_dump_state() function exists.
That's what we usually do when new features are introduced. And there's
actually a bug there: you have the check backwards. (try dumping a
database with any sequences in it; it fails)

Right.

* pg_dump should not output a USING clause when a sequence uses the
'local' sequence. No point in adding such noise - making the SQL command
not standard-compatible - for the 99% of databases that don't use other
sequence AMs.

Well this only works if we remove the GUC. Because otherwise if GUC is
there then you always need to either add USING clause or set the GUC in
advance (like we do with search_path for example).

* Be careful to escape all strings correctly in pg_dump. I think you're
missing escaping for the 'state' variable at least.

Ouch.

In sequence_save_tuple:

else
{
/*
* New tuple was not sent, so the original tuple was probably
just
* changed inline, all we need to do is mark the buffer dirty and
* optionally log the update tuple.
*/
START_CRIT_SECTION();

MarkBufferDirty(seqh->buf);

if (do_wal)
log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);

END_CRIT_SECTION();
}

This is wrong when checksums are enabled and !do_wal. I believe this
should use MarkBufferDirtyHint().

Oh ok, didn't realize.

Notable changes:
- The gapless sequence rewritten to use the transactional storage as
that's the only way to guarantee gaplessness between dump and restore.

Can you elaborate?

Using the auxiliary seqam_gapless_values is a bit problematic. First of
all, the event trigger on DROP SEQUENCE doesn't fire if you drop the
whole schema containing the sequence with DROP SCHEMA CASCADE. Secondly,

Yeah but at worst there are some unused rows there, it's not too bad. I
could also create relation per sequence so that dependency code would
handle everything correctly, but seems bit too expensive to create not
one but two relations per sequence...

updating a row in a table for every nextval() call is pretty darn
expensive.

Yes it's expensive, but the gapless sequence also serializes access so
it will never be speed daemon.

But I don't actually see a problem with dump and restore.

The problem is that the tuple stored in the sequence relation is always
the one with latest state (and with frozen xid), so pg_dump has no way
of getting the value as it was at the time it took the snapshot. This
means that after dump/restore cycle, the sequence can be further away
than the table it's attached to and you end up with a gap there.

Can you rewrite it without using the values table? AFAICS, there are a
two of problems to solve:

1. If the top-level transaction aborts, you need to restore the old
value. I suggest keeping two values in the sequence tuple: the old,
definitely committed one, and the last value. The last value is only
considered current if the associated XID committed; otherwise the old
value is current. When a transaction is about to commit, it stores its
top-level XID and the new value in the "last" field, and copies the
previously current value to the "old" field.

Yes, this is how the previous implementation worked.

2. You need to track the last value on a per-subtransaction basis, until
the transaction commits/rolls back, in order to have "rollback to
savepoint" to retreat the sequence's value. That can be done in
backend-private memory, maintaining a stack of subtransactions and the
last value of each. Use RegisterSubXactCallback to hook into subxact
commit/abort. Just before top-level commit (in pre-commit callback),
update the sequence tuple with the latest value, so that it gets
WAL-logged before the commit record.

I started writing something like what you described here but then I
realized the dump/restore problem and went with the values table which
solves both of these issues at the same time.

BTW I am happy to discuss this at FOSDEM if you are interested and will
have time.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#64Petr Jelinek
petr@2ndquadrant.com
In reply to: Petr Jelinek (#63)
3 attachment(s)
Re: Sequence Access Method WIP

Hi,

sending new version that is updated along the lines of what we discussed
at FOSDEM, which means:

- back to single bytea amdata column (no custom columns)

- the dump/restore interfaces were changed to produce/accept array of
key/value pairs

- psql shows the output of dump interface in verbose mode (this makes it
easier to inspect the state)

- last_value and is_called are always present columns even if the
sequence am does not care for them (this helps with backwards
compatibility with various admin tools)

- pg_dump uses new interface for dumping only for non-local sequences so
that dumps of schemas which are not using custom seqams are restorable
to older postgres

- the default_sequenceam was changed to serial_sequenceam and only
applies to serial/bigserial columns - I would personally prefer to have
the old default but default for serial is better than nothing

- added description of pg_seqam catalog to docs

- gapless_seq still uses table internally to support snapshots for pg_dump

- pg_dump support for dumping the sequence ams

It would be nice to also have something along the lines of chapter 55.2.
(Index Access Method Functions), I plan to send that as additional patch
in few days.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-seqam-v7.patchtext/x-diff; name=0001-seqam-v7.patchDownload
>From 8c168bc37288b1856cb94bc25442fe8aef13268f Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Mon, 22 Sep 2014 19:22:40 +0200
Subject: [PATCH 1/3] seqam v7

---
 doc/src/sgml/catalogs.sgml                    |   94 ++
 doc/src/sgml/config.sgml                      |   21 +
 doc/src/sgml/ref/alter_sequence.sgml          |   19 +
 doc/src/sgml/ref/create_sequence.sgml         |   12 +
 src/backend/access/Makefile                   |    3 +-
 src/backend/access/common/reloptions.c        |   12 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqam.c           |  380 ++++++++
 src/backend/access/sequence/seqlocal.c        |  339 +++++++
 src/backend/catalog/Makefile                  |    2 +-
 src/backend/catalog/objectaddress.c           |    1 +
 src/backend/commands/indexcmds.c              |    2 +-
 src/backend/commands/sequence.c               | 1271 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |    5 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   78 +-
 src/backend/parser/parse_utilcmd.c            |    3 +
 src/backend/utils/cache/catcache.c            |    6 +-
 src/backend/utils/cache/relcache.c            |   62 +-
 src/backend/utils/cache/syscache.c            |   23 +
 src/backend/utils/misc/guc.c                  |   12 +
 src/backend/utils/misc/postgresql.conf.sample |    1 +
 src/bin/pg_dump/pg_dump.c                     |  112 ++-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/psql/describe.c                       |  112 ++-
 src/include/access/reloptions.h               |    7 +-
 src/include/access/seqam.h                    |   67 ++
 src/include/catalog/indexing.h                |    5 +
 src/include/catalog/pg_proc.h                 |   18 +
 src/include/catalog/pg_seqam.h                |   74 ++
 src/include/commands/sequence.h               |   12 +-
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/utils/guc.h                       |    1 +
 src/include/utils/rel.h                       |   25 +-
 src/include/utils/syscache.h                  |    2 +
 src/test/regress/expected/sanity_check.out    |    1 +
 src/test/regress/expected/sequence.out        |   28 +-
 src/test/regress/expected/updatable_views.out |    2 +-
 src/test/regress/sql/sequence.sql             |    4 +-
 40 files changed, 2266 insertions(+), 584 deletions(-)
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqam.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqam.h
 create mode 100644 src/include/catalog/pg_seqam.h

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 515a40e..9e27980 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -249,6 +249,11 @@
      </row>
 
      <row>
+      <entry><link linkend="catalog-pg-seqam"><structname>pg_seqam</structname></link></entry>
+      <entry>sequence access methods</entry>
+     </row>
+
+     <row>
       <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
       <entry>dependencies on shared objects</entry>
      </row>
@@ -5521,6 +5526,95 @@
   </table>
  </sect1>
 
+ <sect1 id="catalog-pg-seqam">
+  <title><structname>pg_seqam</structname></title>
+
+  <indexterm zone="catalog-pg-seqam">
+   <primary>pg_am</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_seqam</structname> stores information about
+   sequence access methods. There is one row for each sequence access method
+   installed on the system.
+  </para>
+
+  <table>
+   <title><structname>pg_seqam</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+
+     <row>
+      <entry><structfield>oid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry></entry>
+      <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamreloptions</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to parse and validate <structfield>reloptions</> for the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqaminit</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function called during initialization or <command>RESET</command> of a sequence</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamalloc</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry><quote>Allocate new sequence id</quote> function</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetval</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function implementing <function>setval()</function> interface</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamgetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to dump current state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to restore a dumped state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="catalog-pg-shdepend">
   <title><structname>pg_shdepend</structname></title>
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6bcb106..bc72602 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5607,6 +5607,27 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-serial-sequenceam" xreflabel="serial_sequenceam">
+      <term><varname>serial_sequenceam</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>serial_sequenceam</> configuration parameter</primary>
+      </indexterm>
+      <indexterm><primary>sequence access method</><secondary>serial</></>
+      </term>
+      <listitem>
+       <para>
+        This variable specifies the default sequence access method to be used
+        for <type>SERIAL</> and <type>BIGSERIAL</>.
+       </para>
+
+       <para>
+        The default is 'local' sequence access method. If the value does not
+        match the name of any existing sequence access method, an error will be
+        raised.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-check-function-bodies" xreflabel="check_function_bodies">
       <term><varname>check_function_bodies</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 9ba9bc4..a5ee41c 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,6 +29,7 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
@@ -221,6 +222,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..c7609c6 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,7 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +224,17 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </refsect1>
 
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..818da15 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+			  transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index f008fab..d8d7b4a 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -821,7 +821,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -852,7 +853,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+			options = am_reloptions(amoptions, datum, false);
+			break;
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1298,14 +1302,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..6292a1b
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,380 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, am_data.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAM data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*serial_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_aminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Method API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_get_state		- dump sequence state (for pg_dump)
+ *		seqam_set_state		- restore sequence state (for pg_dump)
+ *
+ * Additionaly, the am_reloptions interface is used for parsing reloptions
+ * that can be used for passing AM specific options.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+extern void
+seqam_init(Oid seqamid, Oid seqrelid, List *seqparams, List *reloptions,
+		   Datum *values, bool *nulls)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple = NULL;
+	Form_pg_seqam seqamForm;
+	FunctionCallInfoData fcinfo;
+	char	   *validnsps[] = {NULL, NULL};
+	Datum       reloptions_transformed;
+	bytea	   *reloptions_parsed;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	fmgr_info(seqamForm->seqaminit, &procedure);
+
+	/* Allow am specific options */
+	validnsps[0] = NameStr(seqamForm->seqamname);
+
+	/*
+	 *  Parse AM-specific options, convert to text array form,
+	 *  retrieve the AM-option function and then validate.
+	 */
+	reloptions_transformed = transformRelOptions((Datum) NULL, reloptions,
+												 NULL, validnsps, false,
+												 false);
+
+	reloptions_parsed = am_reloptions(seqamForm->seqamreloptions,
+									  reloptions_transformed, true);
+
+	/*
+	 * Have the seqam's proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = ObjectIdGetDatum(seqrelid);
+	fcinfo.arg[1] = PointerGetDatum(seqparams);
+	fcinfo.arg[2] = PointerGetDatum(reloptions_parsed);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.arg[4] = PointerGetDatum(nulls);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[4] = false;
+
+	FunctionCallInvoke(&fcinfo);
+
+	ReleaseSysCache(tuple);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(nrequested);
+	fcinfo.arg[3] = PointerGetDatum(last);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 3, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(new_value);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * seqam_get_state - pg_dump support
+ */
+int
+seqam_get_state(Relation seqrel, SequenceHandle *seqh, char ***keys,
+				char ***values)
+{
+	FmgrInfo	procedure;
+	Datum		count;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamgetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = PointerGetDatum(keys);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+
+	count = FunctionCallInvoke(&fcinfo);
+
+	return DatumGetInt32(count);
+}
+
+/*
+ * seqam_set_state - restore from pg_dump
+ */
+void
+seqam_set_state(Relation seqrel, SequenceHandle *seqh, char **keys,
+				char **values, int count)
+{
+	FmgrInfo	procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamsetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = PointerGetDatum(keys);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.arg[4] = Int32GetDatum(count);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[4] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new serial_seqam value */
+bool
+check_serial_seqam(char **newval, void **extra, GucSource source)
+{
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.  Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence access method \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence access method \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..8f06348
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,339 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct FormLocalSequence
+{
+	int64           log_cnt;
+} FormLocalSequence;
+
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum   reloptions = PG_GETARG_DATUM(0);
+	bool	validate = PG_GETARG_BOOL(1);
+	bytea  *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	Oid		seqrelid = PG_GETARG_OID(0);
+	List   *seqoptions = (List *) PG_GETARG_POINTER(1);
+	Datum  *values = (Datum *) PG_GETARG_POINTER(3);
+	bool   *nulls = (bool *) PG_GETARG_POINTER(4);
+	bool	found_restart;
+	int64	start_value,
+			last_value,
+			min_value,
+			max_value;
+	FormLocalSequence *localseq;
+
+	/* Get the new value to use as starting point. */
+	start_value = DatumGetInt64(values[SEQ_COL_STARTVAL - 1]);
+	start_value = sequence_get_restart_value(seqoptions, start_value,
+											 &found_restart);
+
+	/*
+	 * If this is new sequence or restart was provided, use starting value,
+	 * otherwise work with our saved value.
+	 */
+	if (seqrelid == InvalidOid || found_restart)
+		last_value = start_value;
+	else
+		last_value = DatumGetInt64(values[SEQ_COL_LASTVAL - 1]);
+
+	/* Validate the min/max against the starting point. */
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+	sequence_check_range(last_value, min_value, max_value);
+
+	/*
+	 * If this is new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (seqrelid == InvalidOid || found_restart)
+	{
+		nulls[SEQ_COL_LASTVAL - 1] = false;
+		nulls[SEQ_COL_CALLED - 1] = false;
+		values[SEQ_COL_LASTVAL - 1] = Int64GetDatum(last_value);
+		values[SEQ_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+
+	if (nulls[SEQ_COL_AMDATA - 1])
+	{
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(FormLocalSequence));
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(FormLocalSequence));
+		nulls[SEQ_COL_AMDATA - 1] = false;
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+	}
+
+	localseq = (FormLocalSequence *)
+		VARDATA_ANY(DatumGetByteaP(values[SEQ_COL_AMDATA - 1]));
+
+	/* We always reset the log_cnt. */
+	localseq->log_cnt = 0;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormData_pg_sequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	FormLocalSequence *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (FormLocalSequence *) VARDATA_ANY(&seq->amdata);
+
+	next = result = seq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = seq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	START_CRIT_SECTION();
+
+	/* Log our cached data. */
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		localseq->log_cnt = 0;
+
+		sequence_save_tuple(seqh, NULL, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = log;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, false);
+
+	END_CRIT_SECTION();
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64				next = PG_GETARG_INT64(2);
+	FormData_pg_sequence  *seq;
+	FormLocalSequence *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (FormLocalSequence *) VARDATA_ANY(&seq->amdata);
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = 0;		/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+Datum
+seqam_local_get_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	char			 ***out_keys = (char ***) PG_GETARG_POINTER(2);
+	char			 ***out_values = (char ***) PG_GETARG_POINTER(3);
+	char			  **keys;
+	char			  **values;
+	FormData_pg_sequence  *seq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	keys = palloc(2 * sizeof(char *));
+	values = palloc(2 * sizeof(char *));
+
+	keys[0] = "last_value";
+	values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+										Int64GetDatum(seq->last_value)));
+
+	keys[1] = "is_called";
+	values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+										BoolGetDatum(seq->is_called)));
+
+	*out_keys = keys;
+	*out_values = values;
+
+	PG_RETURN_INT32(2);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumpred state of local sequence (used by pg_dump)
+*/
+Datum
+seqam_local_set_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			  **keys = (char **) PG_GETARG_POINTER(2);
+	char			  **values = (char **) PG_GETARG_POINTER(3);
+	int					count = PG_GETARG_INT32(4);
+	FormData_pg_sequence  *seq;
+	int					i;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	for (i = 0; i < count; i++)
+	{
+		if (pg_strcasecmp(keys[i], "last_value") == 0)
+			seq->last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+												CStringGetDatum(values[i])));
+		else if (pg_strcasecmp(keys[i], "is_called") == 0)
+			seq->is_called = DatumGetBool(DirectFunctionCall1(boolin,
+												CStringGetDatum(values[i])));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for local sequence",
+							keys[i])));
+	}
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a403c64..147c571 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 825d8b2..2412a24 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index e859669..719a5f8 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -526,7 +526,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 622ccf7..bfa42da 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,15 +14,21 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -35,19 +41,13 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -80,6 +80,14 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTupleData	tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -90,13 +98,13 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static Oid get_new_seqam_oid(Oid oldAM, char *accessMethod);
 
 
 /*
@@ -110,6 +118,7 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	Relation	rel;
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
@@ -205,11 +214,6 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "cache_value";
 				value[i - 1] = Int64GetDatumFast(new.cache_value);
 				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
 			case SEQ_COL_CYCLE:
 				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
 				coldef->colname = "is_cycled";
@@ -220,6 +224,12 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "is_called";
 				value[i - 1] = BoolGetDatum(false);
 				break;
+			case SEQ_COL_AMDATA:
+				coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+				coldef->colname = "amdata";
+				null[i - 1] = true;
+				value[i - 1] = (Datum) 0;
+				break;
 		}
 		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
@@ -232,9 +242,22 @@ DefineSequence(CreateSeqStmt *seq)
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
 
+	/* Let AM fill the values[] and nulls[] for the tuple as well. */
+	seqamid = get_new_seqam_oid(InvalidOid, seq->accessMethod);
+	seqam_init(seqamid, InvalidOid, seq->options, seq->amoptions,
+			   value, null);
+
 	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
 	Assert(seqoid != InvalidOid);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	rel = heap_open(seqoid, AccessExclusiveLock);
 	tupDesc = RelationGetDescr(rel);
 
@@ -251,6 +274,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return seqoid;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -264,58 +288,70 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	HeapTuple	newtup;
+	Relation	seqrel;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle seqh;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
-	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	seqrel = seqh.rel;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	memset(replaces, true, tupDesc->natts * sizeof(bool));
+
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+
+	seqam_init(seqrel->rd_rel->relam, seqrelid,
+			   list_make1(makeDefElem("restart", NULL)), NULL,
+			   values, nulls);
+
+	newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqh.rel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqh.rel, newtup);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
 }
 
 /*
@@ -368,23 +404,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,18 +419,26 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 Oid
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
 	SeqTable	elm;
+	HeapTuple	tuple;
+	HeapTuple	newtup;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	List	   *seqoptions;
+	SequenceHandle seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -418,66 +446,111 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidOid;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
-
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqoptions = stmt->options;
+	init_params(seqoptions, false, new, &owned_by);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
 
-	MarkBufferDirty(buf);
+	oldamid = seqrel->rd_rel->relam;
+	seqamid = get_new_seqam_oid(seqrel->rd_rel->relam, stmt->accessMethod);
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
+	/*
+	 * If we are changing sequence AM, we need to alter
+	 * the sequence relation.
+	 */
+	if (seqamid != oldamid)
 	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		bool	found;
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 *
+		 * This may not be the most beautiful way to do it but since
+		 * sequence AMs are expected to handle the RESTART option anyway,
+		 * it does not seem neccessary to invent special parameter for the
+		 * init API just for this.
+		 */
+		(void) sequence_get_restart_value(seqoptions, 0, &found);
+		if (!found)
+		{
+			DefElem	   *defel;
+			int64		last,
+						restart_value;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+			restart_value = seqam_alloc(seqrel, &seqh, 1, &last);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+			defel = makeDefElem("restart", (Node *)makeInteger(restart_value));
+			seqoptions = lcons(defel, seqoptions);
+		}
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
 
-		PageSetLSN(page, recptr);
-	}
+		/*
+		 * Create a new storage file for the sequence.
+		 * See ResetSequence for more info.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
 
-	END_CRIT_SECTION();
+		/* Let the new sequence AM initialize. */
+		seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+				   values, nulls);
 
-	UnlockReleaseBuffer(buf);
+		/* Rebuild the sequence tuple and save it. */
+		newtup = heap_form_tuple(tupDesc, values, nulls);
+		fill_seq_with_data(seqh.rel, newtup);
+
+		/* Update the catalog. */
+		seqrel_update_relam(seqrelid, seqamid);
+	}
+	else
+	{
+		/* Let sequence AM update the tuple. */
+		replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+		memset(replaces, true, tupDesc->natts * sizeof(bool));
+		seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+				   values, nulls);
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
+		sequence_save_tuple(&seqh, newtup, true);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	elm->cached = elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
-	return relid;
+	return seqrelid;
 }
 
 
@@ -516,29 +589,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -556,121 +624,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -679,91 +641,38 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return result;
 }
 
+
 Datum
 currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -804,31 +713,24 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -838,88 +740,27 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	if (!seqrel->rd_islocaltemp)
 		PreventCommandIfReadOnly("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam_setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
-}
-
-/*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
+	last_used_seq = elm;
 
-	do_setval(relid, next, true);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
 /*
  * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
 setval3_oid(PG_FUNCTION_ARGS)
@@ -927,25 +768,70 @@ setval3_oid(PG_FUNCTION_ARGS)
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
 	bool		iscalled = PG_GETARG_BOOL(2);
+	char	   *keys[2] = {"last_value", "is_called"};
+	char	   *values[2];
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
 
-	do_setval(relid, next, iscalled);
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
 
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-/*
- * Open the sequence and acquire AccessShareLock if needed
- *
- * If we haven't touched the sequence already in this transaction,
- * we need to acquire AccessShareLock.  We arrange for the lock to
- * be owned by the top transaction, so that we don't need to do it
- * more than once per xact.
- */
-static Relation
-open_share_lock(SeqTable seq)
-{
-	LocalTransactionId thislxid = MyProc->lxid;
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Convert the data into 'local' sequence dump format and call restore API. */
+	values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+													Int64GetDatum(next)));
+	values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+													BoolGetDatum(iscalled)));
+	seqam_set_state(seqh.rel, &seqh, keys, values, 2);
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
+
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
+
+	sequence_close(&seqh);
+
+	PG_RETURN_INT64(next);
+}
+
+/*
+ * Open the sequence and acquire AccessShareLock if needed
+ *
+ * If we haven't touched the sequence already in this transaction,
+ * we need to acquire AccessShareLock.  We arrange for the lock to
+ * be owned by the top transaction, so that we don't need to do it
+ * more than once per xact.
+ */
+static Relation
+open_share_lock(SeqTable seq)
+{
+	LocalTransactionId thislxid = MyProc->lxid;
 
 	/* Get the lock if not already held in this xact */
 	if (seq->lxid != thislxid)
@@ -992,21 +878,20 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
 {
-	SeqTable	elm;
-	Relation	seqrel;
-	bool		found;
+	SeqTable		elm;
+	Relation		seqrel;
+	bool			found;
 
 	/* Find or create a hash table entry for this sequence */
 	if (seqhashtab == NULL)
 		create_seq_hashtable();
 
-	elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);
+	elm = (SeqTable) hash_search(seqhashtab, &seqrelid, HASH_ENTER, &found);
 
 	/*
 	 * Initialize the new hash table entry if it did not exist already.
@@ -1048,44 +933,57 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	sequence_release_tuple(seqh);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
+	Form_pg_sequence seq_form;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+	{
+		return &seqh->tup;
+	}
 
-	page = BufferGetPage(*buf);
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of seqh->tup */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1095,33 +993,139 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
 	}
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	seq_form = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
 
 	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	seqh->elm->increment = seq_form->increment_by;
+
+	return &seqh->tup;
+}
+
+/*
+ * Update the page, optionally do WAL logging of the tuple
+ */
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal)
+{
+	Page	page;
+
+	Assert(seqh->tup.t_data != NULL);
+
+	page = BufferGetPage(seqh->buf);
+
+	if (HeapTupleIsValid(newtup))
+	{
+		/*
+		 * New tuple was passed, we must process it and replace the old one on
+		 * the same page.
+		 */
+		Page	temppage;
+
+		/* Sequence tuples are always frozen. */
+		HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+		HeapTupleHeaderSetXminFrozen(newtup->t_data);
+		HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+		HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+		newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+		ItemPointerSet(&newtup->t_data->t_ctid, 0, FirstOffsetNumber);
+
+		/*
+		 * Replace the original tuple on the page.
+		 */
+		temppage = PageGetTempPageCopySpecial(page);
+
+		if (PageAddItem(temppage, (Item) newtup->t_data, newtup->t_len,
+					FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+			elog(PANIC, "sequence_save_tuple: failed to add item to page");
+
+		PageSetLSN(temppage, PageGetLSN(page));
+
+		START_CRIT_SECTION();
+
+		PageRestoreTempPage(temppage, page);
+		seqh->tup.t_data = newtup->t_data;
+		seqh->tup.t_len = newtup->t_len;
+		MarkBufferDirtyHint(seqh->buf, true);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, newtup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+	else
+	{
+		/*
+		 * New tuple was not sent, so the original tuple was probably just
+		 * changed inline, all we need to do is mark the buffer dirty and
+		 * optionally log the update tuple.
+		 */
+		START_CRIT_SECTION();
+
+		MarkBufferDirtyHint(seqh->buf, true);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+}
+
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
+	{
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
+	}
+}
+
+/*
+ * Returns true if sequence was not WAL logged since checkpoint
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr = GetRedoRecPtr();
+
+	Assert(BufferIsValid(seqh->buf));
+
+	page = BufferGetPage(seqh->buf);
 
-	return seq;
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
+ *
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1131,13 +1135,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1208,13 +1212,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1223,7 +1220,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1233,7 +1229,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1242,7 +1237,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1250,14 +1244,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1265,7 +1257,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1319,48 +1310,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1375,14 +1324,13 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1398,8 +1346,7 @@ process_owned_by(Relation seqrel, List *owned_by)
 
 	nnames = list_length(owned_by);
 	Assert(nnames > 0);
-	if (nnames == 1)
-	{
+	if (nnames == 1)	{
 		/* Must be OWNED BY NONE */
 		if (strcmp(strVal(linitial(owned_by)), "none") != 0)
 			ereport(ERROR,
@@ -1486,20 +1433,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1517,7 +1461,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1525,12 +1469,184 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *result;
+	int			count;
+	char	  **keys;
+	char	  **values;
+	int			i;
+	Datum	   *out_datums;
+	bool	   *out_nulls;
+	int			dims[1];
+	int			lbs[1];
+	SequenceHandle seqh;
+
+	sequence_open(relid, &seqh);
+
+	count = seqam_get_state(seqh.rel, &seqh, &keys, &values);
+
+	sequence_close(&seqh);
+
+	out_datums = palloc(count * 2 * sizeof(Datum));
+	out_nulls = palloc(count * 2 * sizeof(bool));
+
+	for (i = 0; i < count; i++)
+	{
+		text	   *key = cstring_to_text(keys[i]);
+
+		out_datums[i * 2] = PointerGetDatum(key);
+		out_nulls[i * 2] = false;
+
+		if (values[i] == NULL)
+		{
+			out_datums[i * 2 + 1] = (Datum) 0;
+			out_nulls[i * 2 + 1] = true;
+		}
+		else
+		{
+			text	   *value = cstring_to_text(values[i]);
+
+			out_datums[i * 2 + 1] = PointerGetDatum(value);
+			out_nulls[i * 2 + 1] = false;
+		}
+	}
+
+	dims[0] = count * 2;
+	lbs[0] = 1;
+
+	result = construct_md_array(out_datums, out_nulls,
+								1, dims, lbs,
+								TEXTOID, -1, false, 'i');
+
+	PG_RETURN_POINTER(result);
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *in_keyvals = PG_GETARG_ARRAYTYPE_P(1);
+	Datum	   *in_datums;
+	bool	   *in_nulls;
+	int			in_count;
+	int			count;
+	int			i;
+	char	  **keys;
+	char	  **values;
+	SequenceHandle seqh;
+
+	Assert(ARR_ELEMTYPE(in_keyvals) == TEXTOID);
+
+	/*
+	 * Do the input checks.
+	 */
+	if (ARR_NDIM(in_keyvals) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state must be one dimensional array")));
+
+	if ((ARR_DIMS(in_keyvals)[0]) % 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state array must have even number of elements")));
+
+	deconstruct_array(in_keyvals,
+					  TEXTOID, -1, false, 'i',
+					  &in_datums, &in_nulls, &in_count);
+
+	count = in_count / 2;
+	keys = palloc(count * sizeof(char *));
+	values = palloc(count * sizeof(char *));
+
+	for (i = 0; i < count; i++)
+	{
+		if (in_nulls[i * 2])
+			ereport(ERROR,
+					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+					 errmsg("null value not allowed for state array key")));
+
+		keys[i] = text_to_cstring(DatumGetTextP(in_datums[i * 2]));
+
+		if (in_nulls[i * 2 + 1])
+			values[i] = NULL;
+		else
+			values[i] = text_to_cstring(DatumGetTextP(in_datums[i * 2 + 1]));
+	}
+
+	/* Call in the sequence. */
+	sequence_open(relid, &seqh);
+
+	seqam_set_state(seqh.rel, &seqh, keys, values, count);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+		CatalogUpdateIndexes(rd, ctup);
+	}
+
+	heap_freetuple(ctup);
+	heap_close(rd, RowExclusiveLock);
+	CommandCounterIncrement();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1582,6 +1698,7 @@ seq_redo(XLogReaderState *record)
 	pfree(localpage);
 }
 
+
 /*
  * Flush cached sequence information.
  */
@@ -1596,3 +1713,161 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+static Oid
+get_new_seqam_oid(Oid oldAM, char *accessMethod)
+{
+
+	if (oldAM && accessMethod == NULL)
+		return oldAM;
+	else if (accessMethod == NULL)
+		return LOCAL_SEQAM_OID;
+	else
+		return get_seqam_oid(accessMethod, false);
+}
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64 next = *value;
+	int64 rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
+						bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
+					 bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 66d5083..3de3c8c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9040,7 +9040,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f1a24f5..ffde815 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3362,7 +3362,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3375,7 +3377,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6e8b308..2d731d6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1568,7 +1568,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1579,7 +1581,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 36dac29..8d6a402 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3531,7 +3532,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3553,7 +3580,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3561,11 +3612,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3624,7 +3698,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 7540043..588b433 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -28,6 +28,7 @@
 
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -399,6 +400,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = serial_seqam;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 2e4d0b3..a648a30 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1db4ba8..291b085 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1055,11 +1057,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1534,6 +1539,39 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for an index relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	MemoryContext	indexcxt;
+	Form_pg_seqam	amform;
+
+	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+									 RelationGetRelationName(rel),
+									 ALLOCSET_SMALL_MINSIZE,
+									 ALLOCSET_SMALL_INITSIZE,
+									 ALLOCSET_SMALL_MAXSIZE);
+	rel->rd_indexcxt = indexcxt;
+
+	rel->rd_aminfo = (RelationAmInfo *)
+		MemoryContextAllocZero(rel->rd_indexcxt,
+							   sizeof(RelationAmInfo));
+
+	/*
+	 * Make a copy of the pg_am entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+}
 
 /*
  *		formrdesc
@@ -4806,6 +4844,22 @@ load_relcache_init_file(bool shared)
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
+		else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+		{
+			MemoryContext indexcxt;
+			Assert(!rel->rd_isnailed);
+			Assert(false);
+
+			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+											 RelationGetRelationName(rel),
+											 ALLOCSET_SMALL_MINSIZE,
+											 ALLOCSET_SMALL_INITSIZE,
+											 ALLOCSET_SMALL_MAXSIZE);
+			rel->rd_indexcxt = indexcxt;
+			/* set up zeroed fmgr-info vectors */
+			rel->rd_aminfo = (RelationAmInfo *)
+				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+		}
 		else
 		{
 			/* Count nailed rels to ensure we have 'em all */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index bd27168..5341a09 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAMNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_seqamname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAMOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9572777..618fca0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2780,6 +2781,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"serial_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence access method for SERIAL and BIGSERIAL column types."),
+			gettext_noop("Defaults to 'local' sequence access method."),
+			GUC_IS_NAME
+		},
+		&serial_seqam,
+		"local",
+		check_serial_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index b053659..2bd1fcf 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -508,6 +508,7 @@
 #default_tablespace = ''		# a tablespace name, '' uses the default
 #temp_tablespaces = ''			# a list of tablespace names, '' uses
 					# only default tablespace
+#serial_sequenceam = 'local'	# default sequence access method for SERIAL
 #check_function_bodies = on
 #default_transaction_isolation = 'read committed'
 #default_transaction_read_only = off
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7e92b74..f759fc5 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "libpq/libpq-fs.h"
@@ -4573,6 +4574,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4624,6 +4626,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4665,6 +4668,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4706,6 +4710,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4747,6 +4752,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4786,6 +4792,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4824,6 +4831,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4862,6 +4870,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4900,6 +4909,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4937,6 +4947,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4970,6 +4981,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4998,6 +5010,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5036,6 +5049,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5088,6 +5102,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5152,6 +5167,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -14570,7 +14589,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14650,6 +14670,37 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.5 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -14690,6 +14741,13 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14756,6 +14814,9 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -14772,16 +14833,26 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.5 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(%s))",
+						  fmtId(tbinfo->dobj.name));
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -14794,14 +14865,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index f42c42d..4b2ce46 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -222,6 +222,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_clas) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index c44e447..41815d3 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1395,30 +1395,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1462,13 +1438,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1797,6 +1815,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1806,12 +1826,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index e7b6bb5..4c28c8c 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -46,8 +46,9 @@ typedef enum relopt_kind
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
 	RELOPT_KIND_BRIN = (1 << 10),
+	RELOPT_KIND_SEQUENCE = (1 << 11),
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -270,8 +271,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
-				 bool validate);
+extern bytea *am_reloptions(RegProcedure amoptions, Datum reloptions,
+						   bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..e53f1ab
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,67 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+extern char *serial_seqam;
+
+extern void seqam_init(Oid seqamid, Oid seqrelid, List *seqparams,
+					   List *reloptions, Datum *values, bool *nulls);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern int seqam_get_state(Relation seqrel, SequenceHandle *seqh,
+						   char ***keys, char ***values);
+void seqam_set_state(Relation seqrel, SequenceHandle *seqh,
+					 char **keys, char **values, int count);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup,
+								bool do_wal);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_local_set_state(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index a680229..09df8c3 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAMNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAMOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9edfdb8..fd8b123 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1819,6 +1819,10 @@ 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 = 3277 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 1009 "2205" _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3278 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 1009" _null_ _null_ _null_ _null_ pg_sequence_set_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_ ));
 DESCR("I/O");
@@ -5080,6 +5084,20 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "26 2281 17 2281 2281" _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_get_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 4 0 23 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ seqam_local_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_set_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "2281 2281 2281 2281 23" _null_ _null_ _null_ _null_ seqam_local_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..5c789e3
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	32
+
+CATALOG(pg_seqam,32)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamgetstate;		/* dump state, used by pg_dump */
+	regproc		seqamsetstate;		/* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						7
+#define Anum_pg_seqam_seqamname				1
+#define Anum_pg_seqam_seqamreloptions		2
+#define Anum_pg_seqam_seqaminit				3
+#define Anum_pg_seqam_seqamalloc			4
+#define Anum_pg_seqam_seqamsetval			5
+#define Anum_pg_seqam_seqamgetstate			6
+#define Anum_pg_seqam_seqamsetstate			7
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 6022 (  local		seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_get_state seqam_local_set_state));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 1baf43d..ee8683f 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -29,9 +29,9 @@ typedef struct FormData_pg_sequence
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
 	bool		is_called;
+	bytea		amdata;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -47,12 +47,12 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 #define SEQ_COL_MAXVALUE		5
 #define SEQ_COL_MINVALUE		6
 #define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_CYCLE			8
+#define SEQ_COL_CALLED			9
+#define SEQ_COL_AMDATA			10
 
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_AMDATA
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -71,6 +71,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern Oid	DefineSequence(CreateSeqStmt *stmt);
 extern Oid	AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b1dfa85..5c82957 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2013,8 +2013,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2022,8 +2024,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 717f46b..19b1c91 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -404,6 +404,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_serial_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 6bd786d..c080487 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -48,10 +49,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
+ * functions, defined by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+	/* pg_am only */
 	FmgrInfo	aminsert;
 	FmgrInfo	ambeginscan;
 	FmgrInfo	amgettuple;
@@ -61,6 +64,14 @@ typedef struct RelationAmInfo
 	FmgrInfo	ammarkpos;
 	FmgrInfo	amrestrpos;
 	FmgrInfo	amcanreturn;
+	FmgrInfo	amcostestimate;
+
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+
+	/* Common */
+	FmgrInfo	amoptions;
 } RelationAmInfo;
 
 
@@ -131,23 +142,25 @@ typedef struct RelationData
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+
 	/*
-	 * index access support info (used only for an index relation)
+	 * Access support info (used only for index or sequence relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
 	 * arrays are indexed by support function number, which is a sufficient
 	 * identifier given that restriction.
 	 *
-	 * Note: rd_amcache is available for index AMs to cache private data about
-	 * an index.  This must be just a cache since it may get reset at any time
+	 * Note: rd_amcache is available for AMs to cache private data about
+	 * an object.  This must be just a cache since it may get reset at any time
 	 * (in particular, it will get reset by a relcache inval message for the
 	 * index).  If used, it must point to a single memory chunk palloc'd in
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
-	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am */
+	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am or pg_seqam */
 	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
@@ -158,7 +171,7 @@ typedef struct RelationData
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
-	void	   *rd_amcache;		/* available for use by index AM */
+	void	   *rd_amcache;		/* available for use by AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
 
 	/*
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index ba0b090..ac04502 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index c7be273..d4e250f 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3982a46 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | f         | \x0000000000000000
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | t         | \x1f00000000000000
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 80c5706..f444e66 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -106,9 +106,9 @@ SELECT table_name, column_name, is_updatable
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
  ro_view19  | is_called     | NO
+ ro_view19  | amdata        | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
  ro_view20  | a             | NO
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..e1273ef 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
-- 
1.9.1

0002-seqam-ddl-v3.patchtext/x-diff; name=0002-seqam-ddl-v3.patchDownload
>From feef921584489c4787f87d50c93eeddac10aefb3 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Sun, 15 Feb 2015 03:00:24 +0100
Subject: [PATCH 2/3] seqam ddl v3

---
 doc/src/sgml/catalogs.sgml           |   6 +
 doc/src/sgml/ref/allfiles.sgml       |   2 +
 doc/src/sgml/ref/create_seqam.sgml   | 163 ++++++++++++++++++
 doc/src/sgml/ref/drop_seqam.sgml     |  87 ++++++++++
 doc/src/sgml/reference.sgml          |   2 +
 src/backend/access/sequence/seqam.c  | 313 +++++++++++++++++++++++++++++++++++
 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            |  13 +-
 src/backend/tcop/utility.c           |  12 ++
 src/bin/pg_dump/common.c             |   5 +
 src/bin/pg_dump/pg_dump.c            | 158 +++++++++++++++++-
 src/bin/pg_dump/pg_dump.h            |   9 +-
 src/bin/pg_dump/pg_dump_sort.c       |  11 +-
 src/include/access/seqam.h           |   3 +
 src/include/catalog/dependency.h     |   1 +
 src/include/nodes/parsenodes.h       |   1 +
 src/include/parser/kwlist.h          |   1 +
 21 files changed, 880 insertions(+), 8 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/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9e27980..4f8c784 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -5539,6 +5539,12 @@
    installed on the system.
   </para>
 
+  <para>
+   The contents of this catalog can be manipulated by
+   <command>CREATE ACCESS METHOD FOR SEQUENCES</> and
+   <command>DROP ACCESS METHOD FOR SEQUENCES</>.
+  </para>
+
   <table>
    <title><structname>pg_seqam</> Columns</title>
 
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..e95409f
--- /dev/null
+++ b/doc/src/sgml/ref/create_seqam.sgml
@@ -0,0 +1,163 @@
+<!--
+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> (
+    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> ,
+    GETSTATE = <replaceable class="parameter">getstate_function</replaceable>,
+    SETSTATE = <replaceable class="parameter">setstate_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">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">getstate_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">setstate_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 6292a1b..d332190 100644
--- a/src/backend/access/sequence/seqam.c
+++ b/src/backend/access/sequence/seqam.c
@@ -67,8 +67,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"
 
@@ -348,6 +360,307 @@ check_serial_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_seqamgetstate:
+			nargs = 4;
+			typeId[1] = INTERNALOID;
+			typeId[2] = INTERNALOID;
+			typeId[3] = INTERNALOID;
+			retTypeId = INT4OID;
+			break;
+
+		case Anum_pg_seqam_seqamsetstate:
+			nargs = 5;
+			typeId[1] = INTERNALOID;
+			typeId[2] = INTERNALOID;
+			typeId[3] = INTERNALOID;
+			typeId[4] = INT4OID;
+			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);
+}
+
+
+/*
+ * make pg_depend entries for a new pg_seqam entry
+ */
+static void
+makeSeqAMDependencies(HeapTuple tuple)
+{
+	Form_pg_seqam	seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+	ObjectAddress	myself,
+					referenced;
+
+
+	myself.classId = SeqAccessMethodRelationId;
+	myself.objectId = HeapTupleGetOid(tuple);
+	myself.objectSubId = 0;
+
+	/* Dependency on extension. */
+	recordDependencyOnCurrentExtension(&myself, false);
+
+	/* 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->seqamgetstate;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.objectId = seqam->seqamsetstate;
+	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* definition)
+{
+	char	   *seqamname = strVal(linitial(names));
+	Oid			seqamoid;
+	ListCell   *pl;
+	Relation	rel;
+	Datum		values[Natts_pg_seqam];
+	bool		nulls[Natts_pg_seqam];
+	HeapTuple	tuple;
+
+	/* 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));
+
+	/*
+	 * 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, "getstate") == 0)
+		{
+			values[Anum_pg_seqam_seqamgetstate - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamgetstate);
+		}
+		else if (pg_strcasecmp(defel->defname, "setstate") == 0)
+		{
+			values[Anum_pg_seqam_seqamsetstate - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamsetstate);
+		}
+		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_seqamgetstate - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method getstate function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamsetstate - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method setstate 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);
+
+	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 bfa42da..8521edd 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1618,9 +1618,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..d5cab3c 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,14 @@ DefineStmt:
 					n->definition = list_make1(makeDefElem("from", (Node *) $5));
 					$$ = (Node *)n;
 				}
+			| CREATE ACCESS METHOD FOR SEQUENCES name AS definition
+				{
+					DefineStmt *n = makeNode(DefineStmt);
+					n->kind = OBJECT_SEQAM;
+					n->defnames = list_make1(makeString($6));
+					n->definition = $8;
+					$$ = (Node *)n;
+				}
 		;
 
 definition: '(' def_list ')'						{ $$ = $2; }
@@ -5625,6 +5634,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 +13366,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..447be02 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,11 @@ ProcessUtilitySlow(Node *parsetree,
 							Assert(stmt->args == NIL);
 							DefineCollation(stmt->defnames, stmt->definition);
 							break;
+						case OBJECT_SEQAM:
+							Assert(list_length(stmt->defnames) == 1);
+							Assert(stmt->args == NIL);
+							DefineSeqAM(stmt->defnames, stmt->definition);
+							break;
 						default:
 							elog(ERROR, "unrecognized define stmt type: %d",
 								 (int) stmt->kind);
@@ -1960,6 +1966,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 +2065,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/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 1a0a587..7f272de 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -103,6 +103,7 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
 	int			numForeignServers;
 	int			numDefaultACLs;
 	int			numEventTriggers;
+	int			numSeqAMs;
 
 	if (g_verbose)
 		write_msg(NULL, "reading schemas\n");
@@ -251,6 +252,10 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
 		write_msg(NULL, "reading policies\n");
 	getPolicies(fout, tblinfo, numTables);
 
+	if (g_verbose)
+		write_msg(NULL, "reading sequence access methods\n");
+	getSeqAMs(fout, &numSeqAMs);
+
 	*numTablesPtr = numTables;
 	return tblinfo;
 }
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index f759fc5..3757767 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -178,6 +178,7 @@ static void dumpEventTrigger(Archive *fout, DumpOptions *dopt, EventTriggerInfo
 static void dumpTable(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpAttrDef(Archive *fout, DumpOptions *dopt, AttrDefInfo *adinfo);
+static void dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *tbinfo);
 static void dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo);
 static void dumpIndex(Archive *fout, DumpOptions *dopt, IndxInfo *indxinfo);
@@ -6035,6 +6036,71 @@ getRules(Archive *fout, int *numRules)
 }
 
 /*
+ * getSeqAMs:
+ *	  read all sequence access methods in the system catalogs and return them
+ *	  in the SeqAMInfo* structure
+ *
+ *	numSeqAMs is set to the number of access methods read in
+ */
+SeqAMInfo *
+getSeqAMs(Archive *fout, int *numSeqAMs)
+{
+	PGresult   *res;
+	int			ntups;
+	int			i;
+	PQExpBuffer query;
+	SeqAMInfo  *seqminfo;
+	int			i_tableoid,
+				i_oid,
+				i_seqamname;
+
+	/* Before 9.5, there were no sequence access methods */
+	if (fout->remoteVersion < 90500)
+	{
+		*numSeqAMs = 0;
+		return NULL;
+	}
+
+	query = createPQExpBuffer();
+
+	appendPQExpBuffer(query,
+					  "SELECT tableoid, oid, seqamname "
+					  "FROM pg_catalog.pg_seqam "
+					  "WHERE oid != '%u'::pg_catalog.oid",
+					  LOCAL_SEQAM_OID);
+
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+	ntups = PQntuples(res);
+	*numSeqAMs = ntups;
+
+	seqminfo = (SeqAMInfo *) pg_malloc(ntups * sizeof(SeqAMInfo));
+
+	i_tableoid = PQfnumber(res, "tableoid");
+	i_oid = PQfnumber(res, "oid");
+	i_seqamname = PQfnumber(res, "seqamname");
+
+	for (i = 0; i < ntups; i++)
+	{
+		seqminfo[i].dobj.objType = DO_SEQAM;
+		seqminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+		seqminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+		AssignDumpId(&seqminfo[i].dobj);
+		seqminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_seqamname));
+		seqminfo[i].dobj.namespace = NULL;
+
+		/* Decide whether we want to dump it */
+		selectDumpableObject(&(seqminfo[i].dobj));
+	}
+
+	PQclear(res);
+
+	destroyPQExpBuffer(query);
+
+	return seqminfo;
+}
+
+/*
  * getTriggers
  *	  get information about every trigger on a dumpable table
  *
@@ -8285,6 +8351,9 @@ dumpDumpableObject(Archive *fout, DumpOptions *dopt, DumpableObject *dobj)
 		case DO_POLICY:
 			dumpPolicy(fout, dopt, (PolicyInfo *) dobj);
 			break;
+		case DO_SEQAM:
+			dumpSeqAM(fout, dopt, (SeqAMInfo *) dobj);
+			break;
 		case DO_PRE_DATA_BOUNDARY:
 		case DO_POST_DATA_BOUNDARY:
 			/* never dumped, nothing to do */
@@ -14578,6 +14647,90 @@ findLastBuiltinOid_V70(Archive *fout)
 }
 
 /*
+ * dumpSeqAM
+ *	  write the declaration of one user-defined sequence access method
+ */
+static void
+dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *seqaminfo)
+{
+	PGresult   *res;
+	PQExpBuffer q;
+	PQExpBuffer delq;
+	PQExpBuffer labelq;
+	PQExpBuffer query;
+	char	   *seqamreloptions;
+	char	   *seqaminit;
+	char	   *seqamalloc;
+	char	   *seqamsetval;
+	char	   *seqamgetstate;
+	char	   *seqamsetstate;
+
+	/* Skip if not to be dumped */
+	if (!seqaminfo->dobj.dump || dopt->dataOnly)
+		return;
+
+	q = createPQExpBuffer();
+	delq = createPQExpBuffer();
+	labelq = createPQExpBuffer();
+	query = createPQExpBuffer();
+
+	appendPQExpBuffer(query, "SELECT seqamreloptions, seqaminit, "
+							 "seqamalloc, seqamsetval, "
+							 "seqamgetstate, seqamsetstate "
+							 "FROM pg_catalog.pg_seqam "
+							 "WHERE oid = '%u'::pg_catalog.oid",
+							 seqaminfo->dobj.catId.oid);
+
+	res = ExecuteSqlQueryForSingleRow(fout, query->data);
+
+	seqamreloptions = PQgetvalue(res, 0, PQfnumber(res, "seqamreloptions"));
+	seqaminit = PQgetvalue(res, 0, PQfnumber(res, "seqaminit"));
+	seqamalloc = PQgetvalue(res, 0, PQfnumber(res, "seqamalloc"));
+	seqamsetval = PQgetvalue(res, 0, PQfnumber(res, "seqamsetval"));
+	seqamgetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamgetstate"));
+	seqamsetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamsetstate"));
+
+	appendPQExpBuffer(q, "CREATE ACCESS METHOD FOR SEQUENCES %s AS (\n",
+					  fmtId(seqaminfo->dobj.name));
+
+	appendPQExpBuffer(q, "    RELOPTIONS = %s,\n", seqamreloptions);
+	appendPQExpBuffer(q, "    INIT = %s,\n", seqaminit);
+	appendPQExpBuffer(q, "    ALLOC = %s,\n", seqamalloc);
+	appendPQExpBuffer(q, "    SETVAL = %s,\n", seqamsetval);
+	appendPQExpBuffer(q, "    GETSTATE = %s,\n", seqamgetstate);
+	appendPQExpBuffer(q, "    SETSTATE = %s\n);\n", seqamsetstate);
+
+	appendPQExpBuffer(delq, "DROP CREATE ACCESS METHOD FOR SEQUENCES %s",
+					  fmtId(seqaminfo->dobj.name));
+
+	appendPQExpBuffer(labelq, "ACCESS METHOD FOR SEQUENCES %s",
+					  fmtId(seqaminfo->dobj.name));
+
+	if (dopt->binary_upgrade)
+		binary_upgrade_extension_member(q, &seqaminfo->dobj, labelq->data);
+
+	ArchiveEntry(fout, seqaminfo->dobj.catId, seqaminfo->dobj.dumpId,
+				 seqaminfo->dobj.name,
+				 NULL,
+				 NULL,
+				 "",
+				 false, "ACCESS METHOD FOR SEQUENCES", SECTION_PRE_DATA,
+				 q->data, delq->data, NULL,
+				 NULL, 0,
+				 NULL, NULL);
+
+	/* Dump Parser Comments */
+	dumpComment(fout, dopt, labelq->data,
+				NULL, "",
+				seqaminfo->dobj.catId, 0, seqaminfo->dobj.dumpId);
+
+	PQclear(res);
+	destroyPQExpBuffer(q);
+	destroyPQExpBuffer(delq);
+	destroyPQExpBuffer(labelq);
+}
+
+/*
  * dumpSequence
  *	  write the declaration (not data) of one user-defined sequence
  */
@@ -14679,8 +14832,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	{
 		PGresult   *res2;
 
-		printfPQExpBuffer(query, "SELECT a.seqamname\n"
-								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+		printfPQExpBuffer(query, "SELECT a.seqamname "
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c "
 								 "WHERE c.relam = a.oid AND c.oid = %u",
 						  tbinfo->dobj.catId.oid);
 
@@ -15705,6 +15858,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
 			case DO_FDW:
 			case DO_FOREIGN_SERVER:
 			case DO_BLOB:
+			case DO_SEQAM:
 				/* Pre-data objects: must come before the pre-data boundary */
 				addObjectDependency(preDataBound, dobj->dumpId);
 				break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 4b2ce46..dd78ca9 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -76,7 +76,8 @@ typedef enum
 	DO_POST_DATA_BOUNDARY,
 	DO_EVENT_TRIGGER,
 	DO_REFRESH_MATVIEW,
-	DO_POLICY
+	DO_POLICY,
+	DO_SEQAM
 } DumpableObjectType;
 
 typedef struct _dumpableObject
@@ -305,6 +306,11 @@ typedef struct _ruleInfo
 	/* reloptions is only set if we need to dump the options with the rule */
 } RuleInfo;
 
+typedef struct _seqamInfo
+{
+	DumpableObject dobj;
+} SeqAMInfo;
+
 typedef struct _triggerInfo
 {
 	DumpableObject dobj;
@@ -551,5 +557,6 @@ extern void getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionIn
 					   int numExtensions);
 extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
 extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);
+extern SeqAMInfo *getSeqAMs(Archive *fout, int *numSeqAMs);
 
 #endif   /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 4b9bba0..9b431dc 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -73,7 +73,8 @@ static const int oldObjectTypePriority[] =
 	13,							/* DO_POST_DATA_BOUNDARY */
 	20,							/* DO_EVENT_TRIGGER */
 	15,							/* DO_REFRESH_MATVIEW */
-	21							/* DO_POLICY */
+	21,							/* DO_POLICY */
+	5							/* DO_SEQAM */
 };
 
 /*
@@ -122,7 +123,8 @@ static const int newObjectTypePriority[] =
 	25,							/* DO_POST_DATA_BOUNDARY */
 	32,							/* DO_EVENT_TRIGGER */
 	33,							/* DO_REFRESH_MATVIEW */
-	34							/* DO_POLICY */
+	34,							/* DO_POLICY */
+	17							/* DO_SEQAM */
 };
 
 static DumpId preDataBoundId;
@@ -1443,6 +1445,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
 					 "POLICY (ID %d OID %u)",
 					 obj->dumpId, obj->catId.oid);
 			return;
+		case DO_SEQAM:
+			snprintf(buf, bufsize,
+					 "ACCESS METHOD FOR SEQUENCES %s  (ID %d OID %u)",
+					 obj->name, obj->dumpId, obj->catId.oid);
+			return;
 		case DO_PRE_DATA_BOUNDARY:
 			snprintf(buf, bufsize,
 					 "PRE-DATA BOUNDARY  (ID %d)",
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
index e53f1ab..436e78f 100644
--- a/src/include/access/seqam.h
+++ b/src/include/access/seqam.h
@@ -28,6 +28,9 @@ typedef struct SequenceHandle SequenceHandle;
 
 extern char *serial_seqam;
 
+extern Oid DefineSeqAM(List *names, List *definition);
+extern void RemoveSeqAMById(Oid seqamoid);
+
 extern void seqam_init(Oid seqamid, Oid seqrelid, List *seqparams,
 					   List *reloptions, Datum *values, bool *nulls);
 extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
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/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5c82957..941adbc 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

0003-gapless-sequence-v4.patchtext/x-diff; name=0003-gapless-sequence-v4.patchDownload
>From b253d05a9781354bd3302e3590f327e18072bdbb Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Tue, 9 Dec 2014 22:57:53 +0100
Subject: [PATCH 3/3] gapless sequence v4

---
 contrib/gapless_seq/Makefile                 |  65 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  71 ++++
 contrib/gapless_seq/gapless_seq.c            | 490 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/gapless_seq.sgml         |  22 ++
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 ++++
 9 files changed, 920 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/gapless_seq.sgml
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql

diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..63b51c4
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,65 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-install=./tmp_check \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq
+	$(pg_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq
+	$(pg_isolation_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..c2dd1be
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+   pg_sequence_get_state    
+----------------------------
+ {last_value,2,is_called,t}
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+   pg_sequence_get_state    
+----------------------------
+ {last_value,2,is_called,t}
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+ERROR:  cannot drop sequence access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on sequence access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..8553e81
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,71 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_init(OID, INTERNAL, BYTEA, INTERNAL, INTERNAL)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_alloc(INTERNAL, INTERNAL, BIGINT, INTERNAL)
+RETURNS BIGINT
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_setval(INTERNAL, INTERNAL, BIGINT)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_get_state(INTERNAL, INTERNAL, INTERNAL, INTERNAL)
+RETURNS INT
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_set_state(INTERNAL, INTERNAL, INTERNAL, INTERNAL, INT)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	is_called bool NOT NULL,
+	last_value bigint NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relam = (
+			SELECT oid FROM pg_seqam WHERE seqamname = 'gapless'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD FOR SEQUENCES gapless AS (
+	reloptions = seqam_local_reloptions,	/* reloptions parser is same as local (no reloptions) */
+	init = seqam_gapless_init,				/* init new gapless sequence */
+	alloc = seqam_gapless_alloc,			/* logs and returns each value... slow */
+	setval = seqam_gapless_setval,			/* setval support */
+	getstate = seqam_gapless_get_state,		/* pgdump support */
+	setstate = seqam_gapless_set_state		/* pgdump support */
+);
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..bc3e439
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,490 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = Gapless functions
+ *
+ *------------------------------------------------------------
+ */
+extern Datum seqam_gapless_init(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_set_state(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_init);
+PG_FUNCTION_INFO_V1(seqam_gapless_alloc);
+PG_FUNCTION_INFO_V1(seqam_gapless_setval);
+PG_FUNCTION_INFO_V1(seqam_gapless_get_state);
+PG_FUNCTION_INFO_V1(seqam_gapless_set_state);
+
+typedef struct FormGaplessSequence
+{
+	uint32		xid;
+} FormGaplessSequence;
+
+typedef struct FormGaplessValue
+{
+	Oid		seqid;
+	bool	is_called;
+	int64	last_value;
+} FormGaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, int64 last_value,
+							   bool is_called, HeapTuple oldtuple);
+
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ * TODO: more checks
+ */
+Datum
+seqam_gapless_init(PG_FUNCTION_ARGS)
+{
+	Oid		seqrelid = PG_GETARG_OID(0);
+	List   *seqoptions = (List *) PG_GETARG_POINTER(1);
+	Datum  *values = (Datum *) PG_GETARG_POINTER(3);
+	bool   *nulls = (bool *) PG_GETARG_POINTER(4);
+	bool	found_restart;
+	int64	start_value,
+			last_value,
+			min_value,
+			max_value;
+	Relation		valrel;
+	HeapTuple		tuple = NULL;
+	TransactionId	local_xid = GetTopTransactionId();
+	FormGaplessSequence *gapless_seq;
+
+	/* Get the new value to use as starting point. */
+	start_value = DatumGetInt64(values[SEQ_COL_STARTVAL - 1]);
+	start_value = sequence_get_restart_value(seqoptions, start_value,
+											 &found_restart);
+
+	/* Load current value if this is existing sequence. */
+	if (seqrelid != InvalidOid)
+	{
+		valrel = open_values_rel();
+		tuple = get_last_value_tup(valrel, seqrelid);
+	}
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we use the starting value
+	 * otherwise we use the stored value.
+	 */
+	if (seqrelid == InvalidOid || found_restart || !HeapTupleIsValid(tuple))
+		last_value = start_value;
+	else
+	{
+		FormGaplessValue *v = (FormGaplessValue *) GETSTRUCT(tuple);
+		last_value = v->last_value;
+	}
+
+	/* Validate the min/max against the starting point. */
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+	sequence_check_range(last_value, min_value, max_value);
+
+	/*
+	 * If this is existing sequence with new RESTART value we should update
+	 * our state to that value.
+	 */
+	if (seqrelid != InvalidOid && found_restart)
+		set_last_value_tup(valrel, seqrelid, last_value, false, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	if (seqrelid != InvalidOid)
+		heap_close(valrel, NoLock);
+
+	/* Update the xid info */
+	if (nulls[SEQ_COL_AMDATA - 1])
+	{
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(FormGaplessSequence));
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(FormGaplessSequence));
+		nulls[SEQ_COL_AMDATA - 1] = false;
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+	}
+
+	gapless_seq = (FormGaplessSequence *)
+		VARDATA_ANY(DatumGetByteaP(values[SEQ_COL_AMDATA - 1]));
+
+	gapless_seq->xid = UInt32GetDatum(local_xid);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+Datum
+seqam_gapless_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	/* we ignore nreguested as gapless sequence can't do caching */
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	FormData_pg_sequence   *seq;
+	FormGaplessSequence	   *gapless_seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	gapless_seq = (FormGaplessSequence *) VARDATA_ANY(&seq->amdata);
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		FormGaplessValue *v = (FormGaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/*
+	 * Insert or update the last value tuple.
+	 */
+	set_last_value_tup(valrel, seqrelid, result, true, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/*
+	 * If current tx is different fron the last one,
+	 * update the sequence tuple as well.
+	 *
+	 * We don't need to WAL log the update as the only thing we save to
+	 * sequence tuple is the active transaction id and we know that in case of
+	 * crash the transaction id will not be active so it's ok to lose the
+	 * update.
+	 */
+	if (gapless_seq->xid != local_xid)
+	{
+		gapless_seq->xid = local_xid;
+		sequence_save_tuple(seqh, NULL, false);
+	}
+
+	*last = result;
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+Datum
+seqam_gapless_setval(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+Datum
+seqam_gapless_get_state(PG_FUNCTION_ARGS)
+{
+	Relation			seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	   *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	char			 ***out_keys = (char ***) PG_GETARG_POINTER(2);
+	char			 ***out_values = (char ***) PG_GETARG_POINTER(3);
+	char			  **keys;
+	char			  **values;
+	Oid					seqrelid = RelationGetRelid(seqrel);
+	Relation			valrel;
+	HeapTuple			tuple;
+	int64				last_value;
+	bool				is_called;
+
+	/*
+	 * Get the last value from the values table, if not found use start_value
+	 * from the sequence definition.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		FormGaplessValue *v = (FormGaplessValue *) GETSTRUCT(tuple);
+		last_value = v->last_value;
+		is_called = v->is_called;
+	}
+	else
+	{
+		FormData_pg_sequence *seq = (FormData_pg_sequence *)
+			GETSTRUCT(sequence_read_tuple(seqh));
+		last_value = seq->start_value;
+		is_called = false;
+	}
+
+	keys = palloc(2 * sizeof(char *));
+	values = palloc(2 * sizeof(char *));
+
+	keys[0] = "last_value";
+	values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+												Int64GetDatum(last_value)));
+
+	keys[1] = "is_called";
+	values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+													BoolGetDatum(is_called)));
+
+	*out_keys = keys;
+	*out_values = values;
+
+	PG_RETURN_INT32(2);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+Datum
+seqam_gapless_set_state(PG_FUNCTION_ARGS)
+{
+	Relation			seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			  **keys = (char **) PG_GETARG_POINTER(2);
+	char			  **values = (char **) PG_GETARG_POINTER(3);
+	int					count = PG_GETARG_INT32(4);
+	Oid					seqrelid = RelationGetRelid(seqrel);
+	Relation			valrel;
+	HeapTuple			tuple;
+	int64				last_value = 0;
+	bool				is_called = false;
+	int					i;
+	FormData_pg_sequence   *seq;
+	FormGaplessSequence	   *gapless_seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	if (count != 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("gapless sequence has to be array of two keys and two values")));
+
+	for (i = 0; i < count; i++)
+	{
+		if (pg_strcasecmp(keys[i], "last_value") == 0)
+			last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+												CStringGetDatum(values[i])));
+		else if (pg_strcasecmp(keys[i], "is_called") == 0)
+			is_called = DatumGetBool(DirectFunctionCall1(boolin,
+												CStringGetDatum(values[i])));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for gapless sequence",
+							keys[i])));
+	}
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	gapless_seq = (FormGaplessSequence *) VARDATA_ANY(&seq->amdata);
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * Insert or update the last value tuple.
+	 */
+	set_last_value_tup(valrel, seqrelid, last_value, is_called, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	gapless_seq->xid = local_xid;
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormData_pg_sequence   *seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	FormGaplessSequence	   *gapless_seq = (FormGaplessSequence *) VARDATA_ANY(&seq->amdata);
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (gapless_seq->xid != local_xid &&
+		   TransactionIdIsInProgress(gapless_seq->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(gapless_seq->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, int64 last_value, bool is_called, HeapTuple oldtuple)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/gapless_seq.sgml b/contrib/gapless_seq/gapless_seq.sgml
new file mode 100644
index 0000000..3eb536f
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.sgml
@@ -0,0 +1,22 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless_seq" xreflabel="gapless_seq">
+ <title>gapless sequence</title>
+
+ <indexterm zone="gapless_seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence.
+  This could be very costly and is not recommended for general
+  usage except in specific applications that require this feature.
+ </para>
+
+</sect1>
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..1913947
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
-- 
1.9.1

#65Robert Haas
robertmhaas@gmail.com
In reply to: Petr Jelinek (#64)
Re: Sequence Access Method WIP

On Sun, Feb 15, 2015 at 1:40 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

sending new version that is updated along the lines of what we discussed at
FOSDEM, which means:

- back to single bytea amdata column (no custom columns)

Why?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#66Petr Jelinek
petr@2ndquadrant.com
In reply to: Robert Haas (#65)
Re: Sequence Access Method WIP

On 17/02/15 23:11, Robert Haas wrote:

On Sun, Feb 15, 2015 at 1:40 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

sending new version that is updated along the lines of what we discussed at
FOSDEM, which means:

- back to single bytea amdata column (no custom columns)

Well, the main argument is still future possibility of moving into
single table for storage. And when we discussed about it in person we
agreed that there is not too big advantage in having separate columns
since there need to be dump/restore state interfaces anyway so you can
see the relevant state via those as we made the output more human
readable (and the psql support reflects that).

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#67Petr Jelinek
petr@2ndquadrant.com
In reply to: Petr Jelinek (#66)
Re: Sequence Access Method WIP

On 18/02/15 02:59, Petr Jelinek wrote:

On 17/02/15 23:11, Robert Haas wrote:

On Sun, Feb 15, 2015 at 1:40 PM, Petr Jelinek <petr@2ndquadrant.com>
wrote:

sending new version that is updated along the lines of what we
discussed at
FOSDEM, which means:

- back to single bytea amdata column (no custom columns)

Well, the main argument is still future possibility of moving into
single table for storage. And when we discussed about it in person we
agreed that there is not too big advantage in having separate columns
since there need to be dump/restore state interfaces anyway so you can
see the relevant state via those as we made the output more human
readable (and the psql support reflects that).

Also makes the patch a little simpler obviously.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#68Petr Jelinek
petr@2ndquadrant.com
In reply to: Petr Jelinek (#64)
4 attachment(s)
Re: Sequence Access Method WIP

Slightly updated version of the patch.

Mainly rebased against current master (there were several conflicts) and
fixed some typos, no real functional change.

I also attached initial version of the API sgml doc.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0004-seqam-api-doc-v1.patchtext/x-diff; name=0004-seqam-api-doc-v1.patchDownload
>From 4217187d5e1dc1e3687e527059d54737f0dee51e Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Sun, 15 Mar 2015 20:00:44 +0100
Subject: [PATCH 4/4] seqam-api-doc v1

---
 doc/src/sgml/filelist.sgml |   1 +
 doc/src/sgml/postgres.sgml |   1 +
 doc/src/sgml/seqam.sgml    | 241 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 243 insertions(+)
 create mode 100644 doc/src/sgml/seqam.sgml

diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 89fff77..2e1142a 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index e378d69..a3e8156 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -249,6 +249,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..e3a5764
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,241 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+  <para>
+   All sequence access method share same storage. The storage API is provided
+   by <productname>PostgreSQL</> backend.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_seqam</structname> system catalog (see
+   <xref linkend="catalog-pg-seqam">).  The contents of a
+   <structname>pg_seqam</structname> row is the name of the access method
+   and are references to
+   <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>
+   entries that identify the functions provided by the access method.
+   functions supplied by the access method.
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The functions defining the sequence access method API are:
+  </para>
+
+  <para>
+<programlisting>
+void
+seqam_init (Oid seqrelid, List *seqparams, bytea *reloptions,
+            Datum *value, bool *nulls);
+</programlisting>
+   Initialize sequence. This function is called both for new sequence
+   initilization and for reinitialization when the sequence access method
+   has been changed by <command>ALTER SEQUENCE</>. The <literal>seqrelid</>
+   is set InvalidOid if new sequence is being initialized, otherwise it points
+   to <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple representing the existing sequence.
+   The <literal>seqparams</> is list of <structname>DefElem</>s as passed to
+   the <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+seqam_reloptions (ArrayType *reloptions, bool validate);
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value, which will be then sent
+   to the <function>seqam_init</> and stored in the catalog.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+seqam_alloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+             int64 *last);
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+bool
+seqam_setval Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+int
+seqam_get_state (Relation seqrel, SequenceHandle *seqh, char ***keys,
+                 char ***values);
+</programlisting>
+   Dump the current state of the sequence. Return value is number of elements
+   in the output arrays <literal>keys</> and <literal>values</> which in turn
+   define the key/value pairs of the current state description. This interface
+   is mainly used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void
+seqam_set_state (Relation seqrel, SequenceHandle *seqh, char **keys,
+                 char **values, int count)
+</programlisting>
+   Restore state of the sequence based on the key/value pairs defined in
+   the <literal>keys</> and <literal>values</> parameters. The
+   <literal>count</> specifies length of the forementioned arrays.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence backend provides following
+   functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh);
+</programlisting>
+   Open sequence with given <literal>seqrelid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh);
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+HeapTuple
+sequence_read_tuple (SequenceHandle *seqh);
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup,
+                    bool do_wal);
+</programlisting>
+   Save the modified sequence tuple. Note that the <literal>newtup</> can be
+   <symbol>NULL</> if the sequence tuple was modified inline.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh);
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_tuple</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh);
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_save_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors);
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value);
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_get_restart_value(List *options, int64 default_value,
+                           bool *found);
+</programlisting>
+   Given the list of <structname>DefElm</> <literal>options</> get the restart
+   value for a sequence. If it was not specified in the <literal>options</>
+   list the <literal>default_value</> will be returned and <literal>found</>
+   will be set to <literal>false</>.
+  </para>
+
+ </sect1>
+
+</chapter>
-- 
1.9.1

0003-gapless-sequence-v4.patchtext/x-diff; name=0003-gapless-sequence-v4.patchDownload
>From f445b293e0dbf31ae531167f90493b9acae65543 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Tue, 9 Dec 2014 22:57:53 +0100
Subject: [PATCH 3/4] gapless sequence v4

---
 contrib/gapless_seq/Makefile                 |  65 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  71 ++++
 contrib/gapless_seq/gapless_seq.c            | 490 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/gapless_seq.sgml         |  22 ++
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 ++++
 9 files changed, 920 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/gapless_seq.sgml
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql

diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..63b51c4
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,65 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-install=./tmp_check \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq
+	$(pg_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --extra-install=contrib/gapless_seq \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq
+	$(pg_isolation_regress_installcheck) \
+	    --extra-install=contrib/gapless_seq \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..c2dd1be
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+   pg_sequence_get_state    
+----------------------------
+ {last_value,2,is_called,t}
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+   pg_sequence_get_state    
+----------------------------
+ {last_value,2,is_called,t}
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+ERROR:  cannot drop sequence access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on sequence access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..8553e81
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,71 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_init(OID, INTERNAL, BYTEA, INTERNAL, INTERNAL)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_alloc(INTERNAL, INTERNAL, BIGINT, INTERNAL)
+RETURNS BIGINT
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_setval(INTERNAL, INTERNAL, BIGINT)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_get_state(INTERNAL, INTERNAL, INTERNAL, INTERNAL)
+RETURNS INT
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_set_state(INTERNAL, INTERNAL, INTERNAL, INTERNAL, INT)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	is_called bool NOT NULL,
+	last_value bigint NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relam = (
+			SELECT oid FROM pg_seqam WHERE seqamname = 'gapless'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD FOR SEQUENCES gapless AS (
+	reloptions = seqam_local_reloptions,	/* reloptions parser is same as local (no reloptions) */
+	init = seqam_gapless_init,				/* init new gapless sequence */
+	alloc = seqam_gapless_alloc,			/* logs and returns each value... slow */
+	setval = seqam_gapless_setval,			/* setval support */
+	getstate = seqam_gapless_get_state,		/* pgdump support */
+	setstate = seqam_gapless_set_state		/* pgdump support */
+);
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..bc3e439
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,490 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = Gapless functions
+ *
+ *------------------------------------------------------------
+ */
+extern Datum seqam_gapless_init(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_set_state(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_init);
+PG_FUNCTION_INFO_V1(seqam_gapless_alloc);
+PG_FUNCTION_INFO_V1(seqam_gapless_setval);
+PG_FUNCTION_INFO_V1(seqam_gapless_get_state);
+PG_FUNCTION_INFO_V1(seqam_gapless_set_state);
+
+typedef struct FormGaplessSequence
+{
+	uint32		xid;
+} FormGaplessSequence;
+
+typedef struct FormGaplessValue
+{
+	Oid		seqid;
+	bool	is_called;
+	int64	last_value;
+} FormGaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, int64 last_value,
+							   bool is_called, HeapTuple oldtuple);
+
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ * TODO: more checks
+ */
+Datum
+seqam_gapless_init(PG_FUNCTION_ARGS)
+{
+	Oid		seqrelid = PG_GETARG_OID(0);
+	List   *seqoptions = (List *) PG_GETARG_POINTER(1);
+	Datum  *values = (Datum *) PG_GETARG_POINTER(3);
+	bool   *nulls = (bool *) PG_GETARG_POINTER(4);
+	bool	found_restart;
+	int64	start_value,
+			last_value,
+			min_value,
+			max_value;
+	Relation		valrel;
+	HeapTuple		tuple = NULL;
+	TransactionId	local_xid = GetTopTransactionId();
+	FormGaplessSequence *gapless_seq;
+
+	/* Get the new value to use as starting point. */
+	start_value = DatumGetInt64(values[SEQ_COL_STARTVAL - 1]);
+	start_value = sequence_get_restart_value(seqoptions, start_value,
+											 &found_restart);
+
+	/* Load current value if this is existing sequence. */
+	if (seqrelid != InvalidOid)
+	{
+		valrel = open_values_rel();
+		tuple = get_last_value_tup(valrel, seqrelid);
+	}
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we use the starting value
+	 * otherwise we use the stored value.
+	 */
+	if (seqrelid == InvalidOid || found_restart || !HeapTupleIsValid(tuple))
+		last_value = start_value;
+	else
+	{
+		FormGaplessValue *v = (FormGaplessValue *) GETSTRUCT(tuple);
+		last_value = v->last_value;
+	}
+
+	/* Validate the min/max against the starting point. */
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+	sequence_check_range(last_value, min_value, max_value);
+
+	/*
+	 * If this is existing sequence with new RESTART value we should update
+	 * our state to that value.
+	 */
+	if (seqrelid != InvalidOid && found_restart)
+		set_last_value_tup(valrel, seqrelid, last_value, false, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	if (seqrelid != InvalidOid)
+		heap_close(valrel, NoLock);
+
+	/* Update the xid info */
+	if (nulls[SEQ_COL_AMDATA - 1])
+	{
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(FormGaplessSequence));
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(FormGaplessSequence));
+		nulls[SEQ_COL_AMDATA - 1] = false;
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+	}
+
+	gapless_seq = (FormGaplessSequence *)
+		VARDATA_ANY(DatumGetByteaP(values[SEQ_COL_AMDATA - 1]));
+
+	gapless_seq->xid = UInt32GetDatum(local_xid);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+Datum
+seqam_gapless_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	/* we ignore nreguested as gapless sequence can't do caching */
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	FormData_pg_sequence   *seq;
+	FormGaplessSequence	   *gapless_seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	gapless_seq = (FormGaplessSequence *) VARDATA_ANY(&seq->amdata);
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		FormGaplessValue *v = (FormGaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/*
+	 * Insert or update the last value tuple.
+	 */
+	set_last_value_tup(valrel, seqrelid, result, true, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/*
+	 * If current tx is different fron the last one,
+	 * update the sequence tuple as well.
+	 *
+	 * We don't need to WAL log the update as the only thing we save to
+	 * sequence tuple is the active transaction id and we know that in case of
+	 * crash the transaction id will not be active so it's ok to lose the
+	 * update.
+	 */
+	if (gapless_seq->xid != local_xid)
+	{
+		gapless_seq->xid = local_xid;
+		sequence_save_tuple(seqh, NULL, false);
+	}
+
+	*last = result;
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+Datum
+seqam_gapless_setval(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+Datum
+seqam_gapless_get_state(PG_FUNCTION_ARGS)
+{
+	Relation			seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	   *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	char			 ***out_keys = (char ***) PG_GETARG_POINTER(2);
+	char			 ***out_values = (char ***) PG_GETARG_POINTER(3);
+	char			  **keys;
+	char			  **values;
+	Oid					seqrelid = RelationGetRelid(seqrel);
+	Relation			valrel;
+	HeapTuple			tuple;
+	int64				last_value;
+	bool				is_called;
+
+	/*
+	 * Get the last value from the values table, if not found use start_value
+	 * from the sequence definition.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		FormGaplessValue *v = (FormGaplessValue *) GETSTRUCT(tuple);
+		last_value = v->last_value;
+		is_called = v->is_called;
+	}
+	else
+	{
+		FormData_pg_sequence *seq = (FormData_pg_sequence *)
+			GETSTRUCT(sequence_read_tuple(seqh));
+		last_value = seq->start_value;
+		is_called = false;
+	}
+
+	keys = palloc(2 * sizeof(char *));
+	values = palloc(2 * sizeof(char *));
+
+	keys[0] = "last_value";
+	values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+												Int64GetDatum(last_value)));
+
+	keys[1] = "is_called";
+	values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+													BoolGetDatum(is_called)));
+
+	*out_keys = keys;
+	*out_values = values;
+
+	PG_RETURN_INT32(2);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+Datum
+seqam_gapless_set_state(PG_FUNCTION_ARGS)
+{
+	Relation			seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			  **keys = (char **) PG_GETARG_POINTER(2);
+	char			  **values = (char **) PG_GETARG_POINTER(3);
+	int					count = PG_GETARG_INT32(4);
+	Oid					seqrelid = RelationGetRelid(seqrel);
+	Relation			valrel;
+	HeapTuple			tuple;
+	int64				last_value = 0;
+	bool				is_called = false;
+	int					i;
+	FormData_pg_sequence   *seq;
+	FormGaplessSequence	   *gapless_seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	if (count != 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("gapless sequence has to be array of two keys and two values")));
+
+	for (i = 0; i < count; i++)
+	{
+		if (pg_strcasecmp(keys[i], "last_value") == 0)
+			last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+												CStringGetDatum(values[i])));
+		else if (pg_strcasecmp(keys[i], "is_called") == 0)
+			is_called = DatumGetBool(DirectFunctionCall1(boolin,
+												CStringGetDatum(values[i])));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for gapless sequence",
+							keys[i])));
+	}
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	gapless_seq = (FormGaplessSequence *) VARDATA_ANY(&seq->amdata);
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * Insert or update the last value tuple.
+	 */
+	set_last_value_tup(valrel, seqrelid, last_value, is_called, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	gapless_seq->xid = local_xid;
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormData_pg_sequence   *seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	FormGaplessSequence	   *gapless_seq = (FormGaplessSequence *) VARDATA_ANY(&seq->amdata);
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (gapless_seq->xid != local_xid &&
+		   TransactionIdIsInProgress(gapless_seq->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(gapless_seq->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, int64 last_value, bool is_called, HeapTuple oldtuple)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/gapless_seq.sgml b/contrib/gapless_seq/gapless_seq.sgml
new file mode 100644
index 0000000..3eb536f
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.sgml
@@ -0,0 +1,22 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless_seq" xreflabel="gapless_seq">
+ <title>gapless sequence</title>
+
+ <indexterm zone="gapless_seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence.
+  This could be very costly and is not recommended for general
+  usage except in specific applications that require this feature.
+ </para>
+
+</sect1>
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..1913947
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
-- 
1.9.1

0002-seqam-ddl-v4.patchtext/x-diff; name=0002-seqam-ddl-v4.patchDownload
>From 8c55371002da1ce15d9b93684feb83da17676af0 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Sun, 15 Feb 2015 03:00:24 +0100
Subject: [PATCH 2/4] seqam ddl v4

---
 doc/src/sgml/catalogs.sgml           |   6 +
 doc/src/sgml/ref/allfiles.sgml       |   2 +
 doc/src/sgml/ref/create_seqam.sgml   | 163 ++++++++++++++++++
 doc/src/sgml/ref/drop_seqam.sgml     |  87 ++++++++++
 doc/src/sgml/reference.sgml          |   2 +
 src/backend/access/sequence/seqam.c  | 313 +++++++++++++++++++++++++++++++++++
 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            |  13 +-
 src/backend/tcop/utility.c           |  12 ++
 src/bin/pg_dump/common.c             |   5 +
 src/bin/pg_dump/pg_dump.c            | 158 +++++++++++++++++-
 src/bin/pg_dump/pg_dump.h            |   9 +-
 src/bin/pg_dump/pg_dump_sort.c       |  11 +-
 src/include/access/seqam.h           |   3 +
 src/include/catalog/dependency.h     |   1 +
 src/include/nodes/parsenodes.h       |   1 +
 src/include/parser/kwlist.h          |   1 +
 21 files changed, 880 insertions(+), 8 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/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index a6605cc..7198d6a 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -5530,6 +5530,12 @@
    installed on the system.
   </para>
 
+  <para>
+   The contents of this catalog can be manipulated by
+   <command>CREATE ACCESS METHOD FOR SEQUENCES</> and
+   <command>DROP ACCESS METHOD FOR SEQUENCES</>.
+  </para>
+
   <table>
    <title><structname>pg_seqam</> Columns</title>
 
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..e95409f
--- /dev/null
+++ b/doc/src/sgml/ref/create_seqam.sgml
@@ -0,0 +1,163 @@
+<!--
+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> (
+    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> ,
+    GETSTATE = <replaceable class="parameter">getstate_function</replaceable>,
+    SETSTATE = <replaceable class="parameter">setstate_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">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">getstate_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">setstate_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 6292a1b..d332190 100644
--- a/src/backend/access/sequence/seqam.c
+++ b/src/backend/access/sequence/seqam.c
@@ -67,8 +67,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"
 
@@ -348,6 +360,307 @@ check_serial_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_seqamgetstate:
+			nargs = 4;
+			typeId[1] = INTERNALOID;
+			typeId[2] = INTERNALOID;
+			typeId[3] = INTERNALOID;
+			retTypeId = INT4OID;
+			break;
+
+		case Anum_pg_seqam_seqamsetstate:
+			nargs = 5;
+			typeId[1] = INTERNALOID;
+			typeId[2] = INTERNALOID;
+			typeId[3] = INTERNALOID;
+			typeId[4] = INT4OID;
+			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);
+}
+
+
+/*
+ * make pg_depend entries for a new pg_seqam entry
+ */
+static void
+makeSeqAMDependencies(HeapTuple tuple)
+{
+	Form_pg_seqam	seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+	ObjectAddress	myself,
+					referenced;
+
+
+	myself.classId = SeqAccessMethodRelationId;
+	myself.objectId = HeapTupleGetOid(tuple);
+	myself.objectSubId = 0;
+
+	/* Dependency on extension. */
+	recordDependencyOnCurrentExtension(&myself, false);
+
+	/* 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->seqamgetstate;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.objectId = seqam->seqamsetstate;
+	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* definition)
+{
+	char	   *seqamname = strVal(linitial(names));
+	Oid			seqamoid;
+	ListCell   *pl;
+	Relation	rel;
+	Datum		values[Natts_pg_seqam];
+	bool		nulls[Natts_pg_seqam];
+	HeapTuple	tuple;
+
+	/* 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));
+
+	/*
+	 * 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, "getstate") == 0)
+		{
+			values[Anum_pg_seqam_seqamgetstate - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamgetstate);
+		}
+		else if (pg_strcasecmp(defel->defname, "setstate") == 0)
+		{
+			values[Anum_pg_seqam_seqamsetstate - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamsetstate);
+		}
+		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_seqamgetstate - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method getstate function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamsetstate - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method setstate 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);
+
+	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 d400ad5..62654f8 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 }
 };
 
 const ObjectAddress InvalidObjectAddress =
@@ -681,6 +695,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;
@@ -915,6 +930,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 */
@@ -975,6 +993,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 */
@@ -1917,6 +1940,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,
@@ -2855,6 +2879,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,
@@ -3332,6 +3371,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;
@@ -4243,6 +4286,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 3fec57e..75ba415 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},
@@ -1084,6 +1085,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:
@@ -1144,6 +1146,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 1089e40..5855e6b 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1637,9 +1637,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 775a4b7..993b4c0 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -590,7 +590,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
@@ -5164,6 +5165,14 @@ DefineStmt:
 					n->definition = list_make1(makeDefElem("from", (Node *) $5));
 					$$ = (Node *)n;
 				}
+			| CREATE ACCESS METHOD FOR SEQUENCES name AS definition
+				{
+					DefineStmt *n = makeNode(DefineStmt);
+					n->kind = OBJECT_SEQAM;
+					n->defnames = list_make1(makeString($6));
+					n->definition = $8;
+					$$ = (Node *)n;
+				}
 		;
 
 definition: '(' def_list ')'						{ $$ = $2; }
@@ -5622,6 +5631,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:
@@ -13485,6 +13495,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 126e38d..9fcc076 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"
@@ -1135,6 +1136,11 @@ ProcessUtilitySlow(Node *parsetree,
 							Assert(stmt->args == NIL);
 							DefineCollation(stmt->defnames, stmt->definition);
 							break;
+						case OBJECT_SEQAM:
+							Assert(list_length(stmt->defnames) == 1);
+							Assert(stmt->args == NIL);
+							DefineSeqAM(stmt->defnames, stmt->definition);
+							break;
 						default:
 							elog(ERROR, "unrecognized define stmt type: %d",
 								 (int) stmt->kind);
@@ -2003,6 +2009,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_POLICY:
 					tag = "DROP POLICY";
 					break;
+				case OBJECT_SEQAM:
+					tag = "DROP ACCESS METHOD FOR SEQUENCES";
+					break;
 				default:
 					tag = "???";
 			}
@@ -2099,6 +2108,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/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 1a0a587..7f272de 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -103,6 +103,7 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
 	int			numForeignServers;
 	int			numDefaultACLs;
 	int			numEventTriggers;
+	int			numSeqAMs;
 
 	if (g_verbose)
 		write_msg(NULL, "reading schemas\n");
@@ -251,6 +252,10 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
 		write_msg(NULL, "reading policies\n");
 	getPolicies(fout, tblinfo, numTables);
 
+	if (g_verbose)
+		write_msg(NULL, "reading sequence access methods\n");
+	getSeqAMs(fout, &numSeqAMs);
+
 	*numTablesPtr = numTables;
 	return tblinfo;
 }
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2b88aab..a54d620 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -178,6 +178,7 @@ static void dumpEventTrigger(Archive *fout, DumpOptions *dopt, EventTriggerInfo
 static void dumpTable(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpAttrDef(Archive *fout, DumpOptions *dopt, AttrDefInfo *adinfo);
+static void dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *tbinfo);
 static void dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo);
 static void dumpIndex(Archive *fout, DumpOptions *dopt, IndxInfo *indxinfo);
@@ -5995,6 +5996,71 @@ getRules(Archive *fout, int *numRules)
 }
 
 /*
+ * getSeqAMs:
+ *	  read all sequence access methods in the system catalogs and return them
+ *	  in the SeqAMInfo* structure
+ *
+ *	numSeqAMs is set to the number of access methods read in
+ */
+SeqAMInfo *
+getSeqAMs(Archive *fout, int *numSeqAMs)
+{
+	PGresult   *res;
+	int			ntups;
+	int			i;
+	PQExpBuffer query;
+	SeqAMInfo  *seqminfo;
+	int			i_tableoid,
+				i_oid,
+				i_seqamname;
+
+	/* Before 9.5, there were no sequence access methods */
+	if (fout->remoteVersion < 90500)
+	{
+		*numSeqAMs = 0;
+		return NULL;
+	}
+
+	query = createPQExpBuffer();
+
+	appendPQExpBuffer(query,
+					  "SELECT tableoid, oid, seqamname "
+					  "FROM pg_catalog.pg_seqam "
+					  "WHERE oid != '%u'::pg_catalog.oid",
+					  LOCAL_SEQAM_OID);
+
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+	ntups = PQntuples(res);
+	*numSeqAMs = ntups;
+
+	seqminfo = (SeqAMInfo *) pg_malloc(ntups * sizeof(SeqAMInfo));
+
+	i_tableoid = PQfnumber(res, "tableoid");
+	i_oid = PQfnumber(res, "oid");
+	i_seqamname = PQfnumber(res, "seqamname");
+
+	for (i = 0; i < ntups; i++)
+	{
+		seqminfo[i].dobj.objType = DO_SEQAM;
+		seqminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+		seqminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+		AssignDumpId(&seqminfo[i].dobj);
+		seqminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_seqamname));
+		seqminfo[i].dobj.namespace = NULL;
+
+		/* Decide whether we want to dump it */
+		selectDumpableObject(&(seqminfo[i].dobj));
+	}
+
+	PQclear(res);
+
+	destroyPQExpBuffer(query);
+
+	return seqminfo;
+}
+
+/*
  * getTriggers
  *	  get information about every trigger on a dumpable table
  *
@@ -8245,6 +8311,9 @@ dumpDumpableObject(Archive *fout, DumpOptions *dopt, DumpableObject *dobj)
 		case DO_POLICY:
 			dumpPolicy(fout, dopt, (PolicyInfo *) dobj);
 			break;
+		case DO_SEQAM:
+			dumpSeqAM(fout, dopt, (SeqAMInfo *) dobj);
+			break;
 		case DO_PRE_DATA_BOUNDARY:
 		case DO_POST_DATA_BOUNDARY:
 			/* never dumped, nothing to do */
@@ -14538,6 +14607,90 @@ findLastBuiltinOid_V70(Archive *fout)
 }
 
 /*
+ * dumpSeqAM
+ *	  write the declaration of one user-defined sequence access method
+ */
+static void
+dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *seqaminfo)
+{
+	PGresult   *res;
+	PQExpBuffer q;
+	PQExpBuffer delq;
+	PQExpBuffer labelq;
+	PQExpBuffer query;
+	char	   *seqamreloptions;
+	char	   *seqaminit;
+	char	   *seqamalloc;
+	char	   *seqamsetval;
+	char	   *seqamgetstate;
+	char	   *seqamsetstate;
+
+	/* Skip if not to be dumped */
+	if (!seqaminfo->dobj.dump || dopt->dataOnly)
+		return;
+
+	q = createPQExpBuffer();
+	delq = createPQExpBuffer();
+	labelq = createPQExpBuffer();
+	query = createPQExpBuffer();
+
+	appendPQExpBuffer(query, "SELECT seqamreloptions, seqaminit, "
+							 "seqamalloc, seqamsetval, "
+							 "seqamgetstate, seqamsetstate "
+							 "FROM pg_catalog.pg_seqam "
+							 "WHERE oid = '%u'::pg_catalog.oid",
+							 seqaminfo->dobj.catId.oid);
+
+	res = ExecuteSqlQueryForSingleRow(fout, query->data);
+
+	seqamreloptions = PQgetvalue(res, 0, PQfnumber(res, "seqamreloptions"));
+	seqaminit = PQgetvalue(res, 0, PQfnumber(res, "seqaminit"));
+	seqamalloc = PQgetvalue(res, 0, PQfnumber(res, "seqamalloc"));
+	seqamsetval = PQgetvalue(res, 0, PQfnumber(res, "seqamsetval"));
+	seqamgetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamgetstate"));
+	seqamsetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamsetstate"));
+
+	appendPQExpBuffer(q, "CREATE ACCESS METHOD FOR SEQUENCES %s AS (\n",
+					  fmtId(seqaminfo->dobj.name));
+
+	appendPQExpBuffer(q, "    RELOPTIONS = %s,\n", seqamreloptions);
+	appendPQExpBuffer(q, "    INIT = %s,\n", seqaminit);
+	appendPQExpBuffer(q, "    ALLOC = %s,\n", seqamalloc);
+	appendPQExpBuffer(q, "    SETVAL = %s,\n", seqamsetval);
+	appendPQExpBuffer(q, "    GETSTATE = %s,\n", seqamgetstate);
+	appendPQExpBuffer(q, "    SETSTATE = %s\n);\n", seqamsetstate);
+
+	appendPQExpBuffer(delq, "DROP ACCESS METHOD FOR SEQUENCES %s",
+					  fmtId(seqaminfo->dobj.name));
+
+	appendPQExpBuffer(labelq, "ACCESS METHOD FOR SEQUENCES %s",
+					  fmtId(seqaminfo->dobj.name));
+
+	if (dopt->binary_upgrade)
+		binary_upgrade_extension_member(q, &seqaminfo->dobj, labelq->data);
+
+	ArchiveEntry(fout, seqaminfo->dobj.catId, seqaminfo->dobj.dumpId,
+				 seqaminfo->dobj.name,
+				 NULL,
+				 NULL,
+				 "",
+				 false, "ACCESS METHOD FOR SEQUENCES", SECTION_PRE_DATA,
+				 q->data, delq->data, NULL,
+				 NULL, 0,
+				 NULL, NULL);
+
+	/* Dump Parser Comments */
+	dumpComment(fout, dopt, labelq->data,
+				NULL, "",
+				seqaminfo->dobj.catId, 0, seqaminfo->dobj.dumpId);
+
+	PQclear(res);
+	destroyPQExpBuffer(q);
+	destroyPQExpBuffer(delq);
+	destroyPQExpBuffer(labelq);
+}
+
+/*
  * dumpSequence
  *	  write the declaration (not data) of one user-defined sequence
  */
@@ -14639,8 +14792,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	{
 		PGresult   *res2;
 
-		printfPQExpBuffer(query, "SELECT a.seqamname\n"
-								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+		printfPQExpBuffer(query, "SELECT a.seqamname "
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c "
 								 "WHERE c.relam = a.oid AND c.oid = %u",
 						  tbinfo->dobj.catId.oid);
 
@@ -15741,6 +15894,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
 			case DO_FDW:
 			case DO_FOREIGN_SERVER:
 			case DO_BLOB:
+			case DO_SEQAM:
 				/* Pre-data objects: must come before the pre-data boundary */
 				addObjectDependency(preDataBound, dobj->dumpId);
 				break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7eaee32..ee68b1d 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -76,7 +76,8 @@ typedef enum
 	DO_POST_DATA_BOUNDARY,
 	DO_EVENT_TRIGGER,
 	DO_REFRESH_MATVIEW,
-	DO_POLICY
+	DO_POLICY,
+	DO_SEQAM
 } DumpableObjectType;
 
 typedef struct _dumpableObject
@@ -304,6 +305,11 @@ typedef struct _ruleInfo
 	/* reloptions is only set if we need to dump the options with the rule */
 } RuleInfo;
 
+typedef struct _seqamInfo
+{
+	DumpableObject dobj;
+} SeqAMInfo;
+
 typedef struct _triggerInfo
 {
 	DumpableObject dobj;
@@ -550,5 +556,6 @@ extern void getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionIn
 					   int numExtensions);
 extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
 extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);
+extern SeqAMInfo *getSeqAMs(Archive *fout, int *numSeqAMs);
 
 #endif   /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index c5ed593..c79ac98 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -73,7 +73,8 @@ static const int oldObjectTypePriority[] =
 	13,							/* DO_POST_DATA_BOUNDARY */
 	20,							/* DO_EVENT_TRIGGER */
 	15,							/* DO_REFRESH_MATVIEW */
-	21							/* DO_POLICY */
+	21,							/* DO_POLICY */
+	5							/* DO_SEQAM */
 };
 
 /*
@@ -122,7 +123,8 @@ static const int newObjectTypePriority[] =
 	25,							/* DO_POST_DATA_BOUNDARY */
 	32,							/* DO_EVENT_TRIGGER */
 	33,							/* DO_REFRESH_MATVIEW */
-	34							/* DO_POLICY */
+	34,							/* DO_POLICY */
+	17							/* DO_SEQAM */
 };
 
 static DumpId preDataBoundId;
@@ -1460,6 +1462,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
 					 "POLICY (ID %d OID %u)",
 					 obj->dumpId, obj->catId.oid);
 			return;
+		case DO_SEQAM:
+			snprintf(buf, bufsize,
+					 "ACCESS METHOD FOR SEQUENCES %s  (ID %d OID %u)",
+					 obj->name, obj->dumpId, obj->catId.oid);
+			return;
 		case DO_PRE_DATA_BOUNDARY:
 			snprintf(buf, bufsize,
 					 "PRE-DATA BOUNDARY  (ID %d)",
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
index e53f1ab..436e78f 100644
--- a/src/include/access/seqam.h
+++ b/src/include/access/seqam.h
@@ -28,6 +28,9 @@ typedef struct SequenceHandle SequenceHandle;
 
 extern char *serial_seqam;
 
+extern Oid DefineSeqAM(List *names, List *definition);
+extern void RemoveSeqAMById(Oid seqamoid);
+
 extern void seqam_init(Oid seqamid, Oid seqrelid, List *seqparams,
 					   List *reloptions, Datum *values, bool *nulls);
 extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
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/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 84b4ae3..d9183f6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1259,6 +1259,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

0001-seqam-v8.patchtext/x-diff; name=0001-seqam-v8.patchDownload
>From 3f056d91b2d8d4bec59474563fdca12061d29823 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Mon, 22 Sep 2014 19:22:40 +0200
Subject: [PATCH 1/4] seqam v8

---
 doc/src/sgml/catalogs.sgml                    |   94 ++
 doc/src/sgml/config.sgml                      |   21 +
 doc/src/sgml/ref/alter_sequence.sgml          |   19 +
 doc/src/sgml/ref/create_sequence.sgml         |   12 +
 src/backend/access/Makefile                   |    3 +-
 src/backend/access/common/reloptions.c        |   12 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqam.c           |  380 ++++++++
 src/backend/access/sequence/seqlocal.c        |  340 +++++++
 src/backend/catalog/Makefile                  |    2 +-
 src/backend/catalog/objectaddress.c           |    1 +
 src/backend/commands/indexcmds.c              |    2 +-
 src/backend/commands/sequence.c               | 1278 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |    5 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   78 +-
 src/backend/parser/parse_utilcmd.c            |    3 +
 src/backend/utils/cache/catcache.c            |    6 +-
 src/backend/utils/cache/relcache.c            |   62 +-
 src/backend/utils/cache/syscache.c            |   23 +
 src/backend/utils/misc/guc.c                  |   12 +
 src/backend/utils/misc/postgresql.conf.sample |    1 +
 src/bin/pg_dump/pg_dump.c                     |  112 ++-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/psql/describe.c                       |  112 ++-
 src/include/access/reloptions.h               |    7 +-
 src/include/access/seqam.h                    |   67 ++
 src/include/catalog/indexing.h                |    5 +
 src/include/catalog/pg_proc.h                 |   18 +
 src/include/catalog/pg_seqam.h                |   74 ++
 src/include/commands/sequence.h               |   12 +-
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/utils/guc.h                       |    1 +
 src/include/utils/rel.h                       |   25 +-
 src/include/utils/syscache.h                  |    2 +
 src/test/regress/expected/sanity_check.out    |    1 +
 src/test/regress/expected/sequence.out        |   28 +-
 src/test/regress/expected/updatable_views.out |    2 +-
 src/test/regress/sql/sequence.sql             |    4 +-
 40 files changed, 2266 insertions(+), 592 deletions(-)
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqam.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqam.h
 create mode 100644 src/include/catalog/pg_seqam.h

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 2325962..a6605cc 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -249,6 +249,11 @@
      </row>
 
      <row>
+      <entry><link linkend="catalog-pg-seqam"><structname>pg_seqam</structname></link></entry>
+      <entry>sequence access methods</entry>
+     </row>
+
+     <row>
       <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
       <entry>dependencies on shared objects</entry>
      </row>
@@ -5512,6 +5517,95 @@
   </table>
  </sect1>
 
+ <sect1 id="catalog-pg-seqam">
+  <title><structname>pg_seqam</structname></title>
+
+  <indexterm zone="catalog-pg-seqam">
+   <primary>pg_am</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_seqam</structname> stores information about
+   sequence access methods. There is one row for each sequence access method
+   installed on the system.
+  </para>
+
+  <table>
+   <title><structname>pg_seqam</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+
+     <row>
+      <entry><structfield>oid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry></entry>
+      <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamreloptions</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to parse and validate <structfield>reloptions</> for the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqaminit</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function called during initialization or <command>RESET</command> of a sequence</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamalloc</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry><quote>Allocate new sequence id</quote> function</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetval</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function implementing <function>setval()</function> interface</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamgetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to dump current state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to restore a dumped state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="catalog-pg-shdepend">
   <title><structname>pg_shdepend</structname></title>
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b30c68d..ce40106 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5671,6 +5671,27 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-serial-sequenceam" xreflabel="serial_sequenceam">
+      <term><varname>serial_sequenceam</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>serial_sequenceam</> configuration parameter</primary>
+      </indexterm>
+      <indexterm><primary>sequence access method</><secondary>serial</></>
+      </term>
+      <listitem>
+       <para>
+        This variable specifies the default sequence access method to be used
+        for <type>SERIAL</> and <type>BIGSERIAL</>.
+       </para>
+
+       <para>
+        The default is 'local' sequence access method. If the value does not
+        match the name of any existing sequence access method, an error will be
+        raised.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-check-function-bodies" xreflabel="check_function_bodies">
       <term><varname>check_function_bodies</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..8078fc7 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,6 +29,7 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
@@ -221,6 +222,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..c7609c6 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,7 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +224,17 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </refsect1>
 
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..818da15 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+			  transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index f008fab..d8d7b4a 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -821,7 +821,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -852,7 +853,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+			options = am_reloptions(amoptions, datum, false);
+			break;
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1298,14 +1302,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..6292a1b
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,380 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, am_data.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAM data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*serial_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_aminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Method API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_get_state		- dump sequence state (for pg_dump)
+ *		seqam_set_state		- restore sequence state (for pg_dump)
+ *
+ * Additionaly, the am_reloptions interface is used for parsing reloptions
+ * that can be used for passing AM specific options.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+extern void
+seqam_init(Oid seqamid, Oid seqrelid, List *seqparams, List *reloptions,
+		   Datum *values, bool *nulls)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple = NULL;
+	Form_pg_seqam seqamForm;
+	FunctionCallInfoData fcinfo;
+	char	   *validnsps[] = {NULL, NULL};
+	Datum       reloptions_transformed;
+	bytea	   *reloptions_parsed;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	fmgr_info(seqamForm->seqaminit, &procedure);
+
+	/* Allow am specific options */
+	validnsps[0] = NameStr(seqamForm->seqamname);
+
+	/*
+	 *  Parse AM-specific options, convert to text array form,
+	 *  retrieve the AM-option function and then validate.
+	 */
+	reloptions_transformed = transformRelOptions((Datum) NULL, reloptions,
+												 NULL, validnsps, false,
+												 false);
+
+	reloptions_parsed = am_reloptions(seqamForm->seqamreloptions,
+									  reloptions_transformed, true);
+
+	/*
+	 * Have the seqam's proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = ObjectIdGetDatum(seqrelid);
+	fcinfo.arg[1] = PointerGetDatum(seqparams);
+	fcinfo.arg[2] = PointerGetDatum(reloptions_parsed);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.arg[4] = PointerGetDatum(nulls);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[4] = false;
+
+	FunctionCallInvoke(&fcinfo);
+
+	ReleaseSysCache(tuple);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(nrequested);
+	fcinfo.arg[3] = PointerGetDatum(last);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+
+	ret = FunctionCallInvoke(&fcinfo);
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, procedure, 3, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = Int64GetDatum(new_value);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * seqam_get_state - pg_dump support
+ */
+int
+seqam_get_state(Relation seqrel, SequenceHandle *seqh, char ***keys,
+				char ***values)
+{
+	FmgrInfo	procedure;
+	Datum		count;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamgetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 4, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = PointerGetDatum(keys);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+
+	count = FunctionCallInvoke(&fcinfo);
+
+	return DatumGetInt32(count);
+}
+
+/*
+ * seqam_set_state - restore from pg_dump
+ */
+void
+seqam_set_state(Relation seqrel, SequenceHandle *seqh, char **keys,
+				char **values, int count)
+{
+	FmgrInfo	procedure;
+	FunctionCallInfoData fcinfo;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamsetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do it's work.
+	 */
+	InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = PointerGetDatum(seqrel);
+	fcinfo.arg[1] = PointerGetDatum(seqh);
+	fcinfo.arg[2] = PointerGetDatum(keys);
+	fcinfo.arg[3] = PointerGetDatum(values);
+	fcinfo.arg[4] = Int32GetDatum(count);
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+	fcinfo.argnull[2] = false;
+	fcinfo.argnull[3] = false;
+	fcinfo.argnull[4] = false;
+
+	FunctionCallInvoke(&fcinfo);
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new serial_seqam value */
+bool
+check_serial_seqam(char **newval, void **extra, GucSource source)
+{
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.  Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence access method \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence access method \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..77edd6b
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,340 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct FormLocalSequence
+{
+	int64           log_cnt;
+} FormLocalSequence;
+
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum   reloptions = PG_GETARG_DATUM(0);
+	bool	validate = PG_GETARG_BOOL(1);
+	bytea  *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	Oid		seqrelid = PG_GETARG_OID(0);
+	List   *seqoptions = (List *) PG_GETARG_POINTER(1);
+	Datum  *values = (Datum *) PG_GETARG_POINTER(3);
+	bool   *nulls = (bool *) PG_GETARG_POINTER(4);
+	bool	found_restart;
+	int64	start_value,
+			last_value,
+			min_value,
+			max_value;
+	FormLocalSequence *localseq;
+
+	/* Get the new value to use as starting point. */
+	start_value = DatumGetInt64(values[SEQ_COL_STARTVAL - 1]);
+	start_value = sequence_get_restart_value(seqoptions, start_value,
+											 &found_restart);
+
+	/*
+	 * If this is new sequence or restart was provided, use starting value,
+	 * otherwise work with our saved value.
+	 */
+	if (seqrelid == InvalidOid || found_restart)
+		last_value = start_value;
+	else
+		last_value = DatumGetInt64(values[SEQ_COL_LASTVAL - 1]);
+
+	/* Validate the min/max against the starting point. */
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+	sequence_check_range(last_value, min_value, max_value);
+
+	/*
+	 * If this is new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (seqrelid == InvalidOid || found_restart)
+	{
+		nulls[SEQ_COL_LASTVAL - 1] = false;
+		nulls[SEQ_COL_CALLED - 1] = false;
+		values[SEQ_COL_LASTVAL - 1] = Int64GetDatum(last_value);
+		values[SEQ_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+
+	if (nulls[SEQ_COL_AMDATA - 1])
+	{
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(FormLocalSequence));
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(FormLocalSequence));
+		nulls[SEQ_COL_AMDATA - 1] = false;
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+	}
+
+	localseq = (FormLocalSequence *)
+		VARDATA_ANY(DatumGetByteaP(values[SEQ_COL_AMDATA - 1]));
+
+	/* We always reset the log_cnt. */
+	localseq->log_cnt = 0;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormData_pg_sequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	FormLocalSequence *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (FormLocalSequence *) VARDATA_ANY(&seq->amdata);
+
+	next = result = seq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = seq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch-rescnt, minv,
+									maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 *
+	 * XXX: Does this need a critical section that would encapsulate both
+	 * changes?
+	 */
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		localseq->log_cnt = 0;
+
+		sequence_save_tuple(seqh, NULL, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = log;			/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, false);
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	int64				next = PG_GETARG_INT64(2);
+	FormData_pg_sequence  *seq;
+	FormLocalSequence *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (FormLocalSequence *) VARDATA_ANY(&seq->amdata);
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = 0;		/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+Datum
+seqam_local_get_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	char			 ***out_keys = (char ***) PG_GETARG_POINTER(2);
+	char			 ***out_values = (char ***) PG_GETARG_POINTER(3);
+	char			  **keys;
+	char			  **values;
+	FormData_pg_sequence  *seq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	keys = palloc(2 * sizeof(char *));
+	values = palloc(2 * sizeof(char *));
+
+	keys[0] = "last_value";
+	values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+										Int64GetDatum(seq->last_value)));
+
+	keys[1] = "is_called";
+	values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+										BoolGetDatum(seq->is_called)));
+
+	*out_keys = keys;
+	*out_values = values;
+
+	PG_RETURN_INT32(2);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumpred state of local sequence (used by pg_dump)
+*/
+Datum
+seqam_local_set_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle	   *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	char			  **keys = (char **) PG_GETARG_POINTER(2);
+	char			  **values = (char **) PG_GETARG_POINTER(3);
+	int					count = PG_GETARG_INT32(4);
+	FormData_pg_sequence  *seq;
+	int					i;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	for (i = 0; i < count; i++)
+	{
+		if (pg_strcasecmp(keys[i], "last_value") == 0)
+			seq->last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+												CStringGetDatum(values[i])));
+		else if (pg_strcasecmp(keys[i], "is_called") == 0)
+			seq->is_called = DatumGetBool(DirectFunctionCall1(boolin,
+												CStringGetDatum(values[i])));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for local sequence",
+							keys[i])));
+	}
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a403c64..147c571 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 142bc68..d400ad5 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 1c1d0da..842b598 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -527,7 +527,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 6d316d6..1089e40 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,6 +14,9 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
@@ -22,8 +25,10 @@
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +41,13 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,6 +80,14 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTupleData	tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -91,13 +98,13 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static Oid get_new_seqam_oid(Oid oldAM, char *accessMethod);
 
 
 /*
@@ -112,6 +119,7 @@ DefineSequence(CreateSeqStmt *seq)
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
 	ObjectAddress address;
+	Oid			seqamid;
 	Relation	rel;
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
@@ -207,11 +215,6 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "cache_value";
 				value[i - 1] = Int64GetDatumFast(new.cache_value);
 				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
 			case SEQ_COL_CYCLE:
 				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
 				coldef->colname = "is_cycled";
@@ -222,6 +225,12 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "is_called";
 				value[i - 1] = BoolGetDatum(false);
 				break;
+			case SEQ_COL_AMDATA:
+				coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+				coldef->colname = "amdata";
+				null[i - 1] = true;
+				value[i - 1] = (Datum) 0;
+				break;
 		}
 		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
@@ -234,10 +243,23 @@ DefineSequence(CreateSeqStmt *seq)
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
 
+	/* Let AM fill the values[] and nulls[] for the tuple as well. */
+	seqamid = get_new_seqam_oid(InvalidOid, seq->accessMethod);
+	seqam_init(seqamid, InvalidOid, seq->options, seq->amoptions,
+			   value, null);
+
 	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	rel = heap_open(seqoid, AccessExclusiveLock);
 	tupDesc = RelationGetDescr(rel);
 
@@ -254,6 +276,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +290,70 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	HeapTuple	newtup;
+	Relation	seqrel;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle seqh;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
-	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	seqrel = seqh.rel;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	memset(replaces, true, tupDesc->natts * sizeof(bool));
+
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+
+	seqam_init(seqrel->rd_rel->relam, seqrelid,
+			   list_make1(makeDefElem("restart", NULL)), NULL,
+			   values, nulls);
+
+	newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqh.rel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqh.rel, newtup);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
 }
 
 /*
@@ -361,7 +396,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/* check the comment above sequence_save_tuple()'s equivalent call. */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +410,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -406,19 +425,27 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
 	SeqTable	elm;
+	HeapTuple	tuple;
+	HeapTuple	newtup;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
 	ObjectAddress address;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	List	   *seqoptions;
+	SequenceHandle seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +453,111 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	seqoptions = stmt->options;
+	init_params(seqoptions, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	oldamid = seqrel->rd_rel->relam;
+	seqamid = get_new_seqam_oid(seqrel->rd_rel->relam, stmt->accessMethod);
+
+	/*
+	 * If we are changing sequence AM, we need to alter
+	 * the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		bool	found;
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 *
+		 * This may not be the most beautiful way to do it but since
+		 * sequence AMs are expected to handle the RESTART option anyway,
+		 * it does not seem neccessary to invent special parameter for the
+		 * init API just for this.
+		 */
+		(void) sequence_get_restart_value(seqoptions, 0, &found);
+		if (!found)
+		{
+			DefElem	   *defel;
+			int64		last,
+						restart_value;
 
-	MarkBufferDirty(buf);
+			restart_value = seqam_alloc(seqrel, &seqh, 1, &last);
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+			defel = makeDefElem("restart", (Node *)makeInteger(restart_value));
+			seqoptions = lcons(defel, seqoptions);
+		}
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		/*
+		 * Create a new storage file for the sequence.
+		 * See ResetSequence for more info.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		/* Let the new sequence AM initialize. */
+		seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+				   values, nulls);
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* Rebuild the sequence tuple and save it. */
+		newtup = heap_form_tuple(tupDesc, values, nulls);
+		fill_seq_with_data(seqh.rel, newtup);
 
-		PageSetLSN(page, recptr);
+		/* Update the catalog. */
+		seqrel_update_relam(seqrelid, seqamid);
+	}
+	else
+	{
+		/* Let sequence AM update the tuple. */
+		replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+		memset(replaces, true, tupDesc->natts * sizeof(bool));
+		seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+				   values, nulls);
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
+		sequence_save_tuple(&seqh, newtup, true);
 	}
 
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	elm->cached = elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +598,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -570,121 +633,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -693,101 +650,38 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for
-	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
-	 * no need to assign xids subxacts, that'll already trigger a appropriate
-	 * wait.  (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return result;
 }
 
+
 Datum
 currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -828,31 +722,24 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -862,92 +749,27 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	if (!seqrel->rd_islocaltemp)
 		PreventCommandIfReadOnly("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam_setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
-}
-
-/*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
+	last_used_seq = elm;
 
-	do_setval(relid, next, true);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
 /*
  * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
 setval3_oid(PG_FUNCTION_ARGS)
@@ -955,15 +777,60 @@ setval3_oid(PG_FUNCTION_ARGS)
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
 	bool		iscalled = PG_GETARG_BOOL(2);
+	char	   *keys[2] = {"last_value", "is_called"};
+	char	   *values[2];
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
 
-	do_setval(relid, next, iscalled);
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
 
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-/*
- * Open the sequence and acquire AccessShareLock if needed
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Convert the data into 'local' sequence dump format and call restore API. */
+	values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+													Int64GetDatum(next)));
+	values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+													BoolGetDatum(iscalled)));
+	seqam_set_state(seqh.rel, &seqh, keys, values, 2);
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
+
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
+
+	sequence_close(&seqh);
+
+	PG_RETURN_INT64(next);
+}
+
+/*
+ * Open the sequence and acquire AccessShareLock if needed
  *
  * If we haven't touched the sequence already in this transaction,
  * we need to acquire AccessShareLock.  We arrange for the lock to
@@ -1020,21 +887,20 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
 {
-	SeqTable	elm;
-	Relation	seqrel;
-	bool		found;
+	SeqTable		elm;
+	Relation		seqrel;
+	bool			found;
 
 	/* Find or create a hash table entry for this sequence */
 	if (seqhashtab == NULL)
 		create_seq_hashtable();
 
-	elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);
+	elm = (SeqTable) hash_search(seqhashtab, &seqrelid, HASH_ENTER, &found);
 
 	/*
 	 * Initialize the new hash table entry if it did not exist already.
@@ -1076,44 +942,57 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	sequence_release_tuple(seqh);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
+	Form_pg_sequence seq_form;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+	{
+		return &seqh->tup;
+	}
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of seqh->tup */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1123,33 +1002,149 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
 	}
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	seq_form = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
 
 	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	seqh->elm->increment = seq_form->increment_by;
 
-	return seq;
+	return &seqh->tup;
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * Update the page, optionally do WAL logging of the tuple
+ */
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal)
+{
+	Page	page;
+
+	Assert(seqh->tup.t_data != NULL);
+
+	page = BufferGetPage(seqh->buf);
+
+	/*
+	 * If something needs to be WAL logged, acquire an xid, so this
+	 * transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait.  (Have to do that here, so we're outside the critical section)
+	 */
+	if (do_wal && RelationNeedsWAL(seqh->rel))
+		GetTopTransactionId();
+
+	if (HeapTupleIsValid(newtup))
+	{
+		/*
+		 * New tuple was passed, we must process it and replace the old one on
+		 * the same page.
+		 */
+		Page	temppage;
+
+		/* Sequence tuples are always frozen. */
+		HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+		HeapTupleHeaderSetXminFrozen(newtup->t_data);
+		HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+		HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+		newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+		ItemPointerSet(&newtup->t_data->t_ctid, 0, FirstOffsetNumber);
+
+		/*
+		 * Replace the original tuple on the page.
+		 */
+		temppage = PageGetTempPageCopySpecial(page);
+
+		if (PageAddItem(temppage, (Item) newtup->t_data, newtup->t_len,
+					FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+			elog(PANIC, "sequence_save_tuple: failed to add item to page");
+
+		PageSetLSN(temppage, PageGetLSN(page));
+
+		START_CRIT_SECTION();
+
+		PageRestoreTempPage(temppage, page);
+		seqh->tup.t_data = newtup->t_data;
+		seqh->tup.t_len = newtup->t_len;
+		MarkBufferDirtyHint(seqh->buf, true);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, newtup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+	else
+	{
+		/*
+		 * New tuple was not sent, so the original tuple was probably just
+		 * changed inline, all we need to do is mark the buffer dirty and
+		 * optionally log the updated tuple.
+		 */
+		START_CRIT_SECTION();
+
+		MarkBufferDirtyHint(seqh->buf, true);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+}
+
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
+	{
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
+	}
+}
+
+/*
+ * Returns true if sequence was not WAL logged since checkpoint
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr = GetRedoRecPtr();
+
+	Assert(BufferIsValid(seqh->buf));
+
+	page = BufferGetPage(seqh->buf);
+
+	return (PageGetLSN(page) <= redoptr);
+}
+
+/*
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
+ *
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1159,13 +1154,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1236,13 +1231,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1251,7 +1239,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1261,7 +1248,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1270,7 +1256,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1278,14 +1263,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1293,7 +1276,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1347,48 +1329,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1403,14 +1343,13 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1426,8 +1365,7 @@ process_owned_by(Relation seqrel, List *owned_by)
 
 	nnames = list_length(owned_by);
 	Assert(nnames > 0);
-	if (nnames == 1)
-	{
+	if (nnames == 1)	{
 		/* Must be OWNED BY NONE */
 		if (strcmp(strVal(linitial(owned_by)), "none") != 0)
 			ereport(ERROR,
@@ -1514,20 +1452,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1545,7 +1480,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1553,12 +1488,184 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *result;
+	int			count;
+	char	  **keys;
+	char	  **values;
+	int			i;
+	Datum	   *out_datums;
+	bool	   *out_nulls;
+	int			dims[1];
+	int			lbs[1];
+	SequenceHandle seqh;
+
+	sequence_open(relid, &seqh);
+
+	count = seqam_get_state(seqh.rel, &seqh, &keys, &values);
+
+	sequence_close(&seqh);
+
+	out_datums = palloc(count * 2 * sizeof(Datum));
+	out_nulls = palloc(count * 2 * sizeof(bool));
+
+	for (i = 0; i < count; i++)
+	{
+		text	   *key = cstring_to_text(keys[i]);
+
+		out_datums[i * 2] = PointerGetDatum(key);
+		out_nulls[i * 2] = false;
+
+		if (values[i] == NULL)
+		{
+			out_datums[i * 2 + 1] = (Datum) 0;
+			out_nulls[i * 2 + 1] = true;
+		}
+		else
+		{
+			text	   *value = cstring_to_text(values[i]);
+
+			out_datums[i * 2 + 1] = PointerGetDatum(value);
+			out_nulls[i * 2 + 1] = false;
+		}
+	}
+
+	dims[0] = count * 2;
+	lbs[0] = 1;
+
+	result = construct_md_array(out_datums, out_nulls,
+								1, dims, lbs,
+								TEXTOID, -1, false, 'i');
+
+	PG_RETURN_POINTER(result);
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *in_keyvals = PG_GETARG_ARRAYTYPE_P(1);
+	Datum	   *in_datums;
+	bool	   *in_nulls;
+	int			in_count;
+	int			count;
+	int			i;
+	char	  **keys;
+	char	  **values;
+	SequenceHandle seqh;
+
+	Assert(ARR_ELEMTYPE(in_keyvals) == TEXTOID);
+
+	/*
+	 * Do the input checks.
+	 */
+	if (ARR_NDIM(in_keyvals) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state must be one dimensional array")));
+
+	if ((ARR_DIMS(in_keyvals)[0]) % 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state array must have even number of elements")));
+
+	deconstruct_array(in_keyvals,
+					  TEXTOID, -1, false, 'i',
+					  &in_datums, &in_nulls, &in_count);
+
+	count = in_count / 2;
+	keys = palloc(count * sizeof(char *));
+	values = palloc(count * sizeof(char *));
+
+	for (i = 0; i < count; i++)
+	{
+		if (in_nulls[i * 2])
+			ereport(ERROR,
+					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+					 errmsg("null value not allowed for state array key")));
+
+		keys[i] = text_to_cstring(DatumGetTextP(in_datums[i * 2]));
+
+		if (in_nulls[i * 2 + 1])
+			values[i] = NULL;
+		else
+			values[i] = text_to_cstring(DatumGetTextP(in_datums[i * 2 + 1]));
+	}
+
+	/* Call in the sequence. */
+	sequence_open(relid, &seqh);
+
+	seqam_set_state(seqh.rel, &seqh, keys, values, count);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+		CatalogUpdateIndexes(rd, ctup);
+	}
+
+	heap_freetuple(ctup);
+	heap_close(rd, RowExclusiveLock);
+	CommandCounterIncrement();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1610,6 +1717,7 @@ seq_redo(XLogReaderState *record)
 	pfree(localpage);
 }
 
+
 /*
  * Flush cached sequence information.
  */
@@ -1624,3 +1732,161 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+static Oid
+get_new_seqam_oid(Oid oldAM, char *accessMethod)
+{
+
+	if (oldAM && accessMethod == NULL)
+		return oldAM;
+	else if (accessMethod == NULL)
+		return LOCAL_SEQAM_OID;
+	else
+		return get_seqam_oid(accessMethod, false);
+}
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64 next = *value;
+	int64 rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
+						bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
+					 bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 623e6bf..6aad928 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9067,7 +9067,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 291e6a7..ca2b5b1 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3373,7 +3373,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3386,7 +3388,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index fcd58ad..28e9886 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1567,7 +1567,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1578,7 +1580,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index cf0d317..775a4b7 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3530,7 +3531,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3552,7 +3579,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3560,11 +3611,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3623,7 +3697,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 1e6da9c..ebb284f 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -28,6 +28,7 @@
 
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -399,6 +400,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = serial_seqam;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 1af43c6..ccb9791 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1db4ba8..291b085 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1055,11 +1057,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1534,6 +1539,39 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for an index relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	MemoryContext	indexcxt;
+	Form_pg_seqam	amform;
+
+	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+									 RelationGetRelationName(rel),
+									 ALLOCSET_SMALL_MINSIZE,
+									 ALLOCSET_SMALL_INITSIZE,
+									 ALLOCSET_SMALL_MAXSIZE);
+	rel->rd_indexcxt = indexcxt;
+
+	rel->rd_aminfo = (RelationAmInfo *)
+		MemoryContextAllocZero(rel->rd_indexcxt,
+							   sizeof(RelationAmInfo));
+
+	/*
+	 * Make a copy of the pg_am entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+}
 
 /*
  *		formrdesc
@@ -4806,6 +4844,22 @@ load_relcache_init_file(bool shared)
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
+		else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+		{
+			MemoryContext indexcxt;
+			Assert(!rel->rd_isnailed);
+			Assert(false);
+
+			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+											 RelationGetRelationName(rel),
+											 ALLOCSET_SMALL_MINSIZE,
+											 ALLOCSET_SMALL_INITSIZE,
+											 ALLOCSET_SMALL_MAXSIZE);
+			rel->rd_indexcxt = indexcxt;
+			/* set up zeroed fmgr-info vectors */
+			rel->rd_aminfo = (RelationAmInfo *)
+				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+		}
 		else
 		{
 			/* Count nailed rels to ensure we have 'em all */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index bd27168..5341a09 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAMNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_seqamname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAMOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 26275bd..33aa08c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2900,6 +2901,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"serial_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence access method for SERIAL and BIGSERIAL column types."),
+			gettext_noop("Defaults to 'local' sequence access method."),
+			GUC_IS_NAME
+		},
+		&serial_seqam,
+		"local",
+		check_serial_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 110983f..a90637b 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -512,6 +512,7 @@
 #default_tablespace = ''		# a tablespace name, '' uses the default
 #temp_tablespaces = ''			# a list of tablespace names, '' uses
 					# only default tablespace
+#serial_sequenceam = 'local'	# default sequence access method for SERIAL
 #check_function_bodies = on
 #default_transaction_isolation = 'read committed'
 #default_transaction_read_only = off
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index fdfb431..2b88aab 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "libpq/libpq-fs.h"
@@ -4533,6 +4534,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4584,6 +4586,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4625,6 +4628,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4666,6 +4670,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4707,6 +4712,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4746,6 +4752,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4784,6 +4791,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4822,6 +4830,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4860,6 +4869,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4897,6 +4907,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4930,6 +4941,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4958,6 +4970,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4996,6 +5009,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5048,6 +5062,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5112,6 +5127,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -14530,7 +14549,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14610,6 +14630,37 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.5 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -14650,6 +14701,13 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14716,6 +14774,9 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -14732,16 +14793,26 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.5 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(%s))",
+						  fmtId(tbinfo->dobj.name));
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -14754,14 +14825,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index a9d3c10..7eaee32 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -221,6 +221,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_clas) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index c44e447..41815d3 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1395,30 +1395,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1462,13 +1438,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1797,6 +1815,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1806,12 +1826,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index e7b6bb5..4c28c8c 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -46,8 +46,9 @@ typedef enum relopt_kind
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
 	RELOPT_KIND_BRIN = (1 << 10),
+	RELOPT_KIND_SEQUENCE = (1 << 11),
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -270,8 +271,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
-				 bool validate);
+extern bytea *am_reloptions(RegProcedure amoptions, Datum reloptions,
+						   bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..e53f1ab
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,67 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+extern char *serial_seqam;
+
+extern void seqam_init(Oid seqamid, Oid seqrelid, List *seqparams,
+					   List *reloptions, Datum *values, bool *nulls);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern int seqam_get_state(Relation seqrel, SequenceHandle *seqh,
+						   char ***keys, char ***values);
+void seqam_set_state(Relation seqrel, SequenceHandle *seqh,
+					 char **keys, char **values, int count);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup,
+								bool do_wal);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_local_set_state(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index a680229..09df8c3 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAMNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAMOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b8a3660..5e3af9e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1823,6 +1823,10 @@ 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 = 3277 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 1009 "2205" _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3278 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 1009" _null_ _null_ _null_ _null_ pg_sequence_set_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_ ));
 DESCR("I/O");
@@ -5086,6 +5090,20 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "26 2281 17 2281 2281" _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_get_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 4 0 23 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ seqam_local_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_set_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "2281 2281 2281 2281 23" _null_ _null_ _null_ _null_ seqam_local_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..5c789e3
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	32
+
+CATALOG(pg_seqam,32)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamgetstate;		/* dump state, used by pg_dump */
+	regproc		seqamsetstate;		/* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						7
+#define Anum_pg_seqam_seqamname				1
+#define Anum_pg_seqam_seqamreloptions		2
+#define Anum_pg_seqam_seqaminit				3
+#define Anum_pg_seqam_seqamalloc			4
+#define Anum_pg_seqam_seqamsetval			5
+#define Anum_pg_seqam_seqamgetstate			6
+#define Anum_pg_seqam_seqamsetstate			7
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 6022 (  local		seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_get_state seqam_local_set_state));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 44862bb..bed8d7d 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -30,9 +30,9 @@ typedef struct FormData_pg_sequence
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
 	bool		is_called;
+	bytea		amdata;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -48,12 +48,12 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 #define SEQ_COL_MAXVALUE		5
 #define SEQ_COL_MINVALUE		6
 #define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_CYCLE			8
+#define SEQ_COL_CALLED			9
+#define SEQ_COL_AMDATA			10
 
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_AMDATA
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +72,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 38ed661..84b4ae3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2037,8 +2037,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2046,8 +2048,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index d3100d1..2bac20d 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -406,6 +406,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_serial_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 6bd786d..c080487 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -48,10 +49,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
+ * functions, defined by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+	/* pg_am only */
 	FmgrInfo	aminsert;
 	FmgrInfo	ambeginscan;
 	FmgrInfo	amgettuple;
@@ -61,6 +64,14 @@ typedef struct RelationAmInfo
 	FmgrInfo	ammarkpos;
 	FmgrInfo	amrestrpos;
 	FmgrInfo	amcanreturn;
+	FmgrInfo	amcostestimate;
+
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+
+	/* Common */
+	FmgrInfo	amoptions;
 } RelationAmInfo;
 
 
@@ -131,23 +142,25 @@ typedef struct RelationData
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+
 	/*
-	 * index access support info (used only for an index relation)
+	 * Access support info (used only for index or sequence relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
 	 * arrays are indexed by support function number, which is a sufficient
 	 * identifier given that restriction.
 	 *
-	 * Note: rd_amcache is available for index AMs to cache private data about
-	 * an index.  This must be just a cache since it may get reset at any time
+	 * Note: rd_amcache is available for AMs to cache private data about
+	 * an object.  This must be just a cache since it may get reset at any time
 	 * (in particular, it will get reset by a relcache inval message for the
 	 * index).  If used, it must point to a single memory chunk palloc'd in
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
-	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am */
+	RelationAmInfo *rd_aminfo;	/* lookup info for funcs found in pg_am or pg_seqam */
 	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
@@ -158,7 +171,7 @@ typedef struct RelationData
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
-	void	   *rd_amcache;		/* available for use by index AM */
+	void	   *rd_amcache;		/* available for use by AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
 
 	/*
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index ba0b090..ac04502 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index c7be273..d4e250f 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3982a46 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | f         | \x0000000000000000
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | t         | \x1f00000000000000
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c49e769..f27bff1 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -106,9 +106,9 @@ SELECT table_name, column_name, is_updatable
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
  ro_view19  | is_called     | NO
+ ro_view19  | amdata        | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
  ro_view20  | a             | NO
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..e1273ef 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
-- 
1.9.1

#69Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Petr Jelinek (#68)
Re: Sequence Access Method WIP

On 03/15/2015 09:07 PM, Petr Jelinek wrote:

Slightly updated version of the patch.

Mainly rebased against current master (there were several conflicts) and
fixed some typos, no real functional change.

I also attached initial version of the API sgml doc.

Thanks!

With the patch, pg_class.relam column references to the pg_seqam table
for sequences, but pg_indexam for indexes. I believe it's the first
instance where we reuse a "foreign key" column like that. It's not a
real foreign key, of course - that wouldn't work with a real foreign key
at all - but it's a bit strange. That makes me a bit uncomfortable. How
do others feel about that?

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#70Andres Freund
andres@anarazel.de
In reply to: Heikki Linnakangas (#69)
Re: Sequence Access Method WIP

On 2015-04-20 12:49:39 +0300, Heikki Linnakangas wrote:

With the patch, pg_class.relam column references to the pg_seqam table for
sequences, but pg_indexam for indexes. I believe it's the first instance
where we reuse a "foreign key" column like that. It's not a real foreign
key, of course - that wouldn't work with a real foreign key at all - but
it's a bit strange. That makes me a bit uncomfortable. How do others feel
about that?

Hm. I'd modeled it more as an extension of the 'relkind' column
mentally. I.e. it further specifies how exactly the relation is
behaving. Given that the field has been added to pg_class and not
pg_index, combined with it not having index in its name, makes me think
that it actually was intended to be used the way it's done in the patch.

It's not the first column that behaves that way btw, at least pg_depend
comes to mind.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#71Petr Jelinek
petr@2ndquadrant.com
In reply to: Andres Freund (#70)
Re: Sequence Access Method WIP

On 20/04/15 12:05, Andres Freund wrote:

On 2015-04-20 12:49:39 +0300, Heikki Linnakangas wrote:

With the patch, pg_class.relam column references to the pg_seqam table for
sequences, but pg_indexam for indexes. I believe it's the first instance
where we reuse a "foreign key" column like that. It's not a real foreign
key, of course - that wouldn't work with a real foreign key at all - but
it's a bit strange. That makes me a bit uncomfortable. How do others feel
about that?

Hm. I'd modeled it more as an extension of the 'relkind' column
mentally. I.e. it further specifies how exactly the relation is
behaving. Given that the field has been added to pg_class and not
pg_index, combined with it not having index in its name, makes me think
that it actually was intended to be used the way it's done in the patch.

That's how I think about it too. It's also short for access method,
nothing really suggests to me that it should be index specific by design.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#72Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Petr Jelinek (#68)
1 attachment(s)
Re: Sequence Access Method WIP

On 03/15/2015 09:07 PM, Petr Jelinek wrote:

Slightly updated version of the patch.

Mainly rebased against current master (there were several conflicts) and
fixed some typos, no real functional change.

I also attached initial version of the API sgml doc.

I finally got around to have another round of review on this. I fixed a
couple of little bugs, did some minor edition on comments etc. See
attached. It is also available in my git repository at
git://git.postgresql.org/git/users/heikki/postgres.git, branch "seqam",
if you want to look at individual changes. It combines your patches 1
and 4, I think those need to be applied together. I haven't looked at
the DDL changes yet.

I'm fairly happy with the alloc API now. I'm not sure it's a good idea
for the AM to access the sequence tuple directly, though. I would seem
cleaner if it only manipulated the amdata Datum. But it's not too bad as
it is.

We went back and forth on whether 'amdata' should be a single column or
multiple columns, but I guess the single bytea column was the consensus
in the end.

The division of labour between sequence.c and the AM, in the init and
the get/set_state functions, is a bit more foggy:

* Do we really need a separate amoptions() method and an init() method,
when the only caller to amoptions() is just before the init() method?
The changes in extractRelOptions suggest that it would call
am_reloptions for sequences too, but that doesn't actually seem to be
happening.

postgres=# create sequence fooseq using local with (garbage=option);
CREATE SEQUENCE

Invalid options probably should throw an error.

* Currently, the AM's init function is responsible for basic sanity
checking, like min < max. It also has to extract the RESTART value from
the list of options. I think it would make more sense to move that to
sequence.c. The AM should only be responsible for initializing the
'amdata' portion, and for handling any AM-specific options. If the AM
doesn't support some options, like MIN and MAX value, it can check them
and throw an error, but it shouldn't be responsible for just passing
them through from the arguments to the sequence tuple.

* It might be better to form the sequence tuple before calling the init
function, and pass the ready-made tuple to it. All the other API
functions deal with the tuple (after calling sequence_read_tuple), so
that would be more consistent. The init function would need to
deconstruct it to modify it, but we're not concerned about performance here.

* The transformations of the arrays in get_state() and set_state()
functions are a bit complicated. The seqam_get_state() function returns
two C arrays, and pg_sequence_get_state() turns them into a text[]
array. Why not construct the text[] array directly in the AM? I guess
it's a bit more convenient to the AM, if the pg_sequence_get_state() do
that, but if that's an issue, you could create generic helper function
to turn two C string arrays into text[], and call that from the AM.

seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));

for (i = 0; i < count; i++)
{
if (pg_strcasecmp(keys[i], "last_value") == 0)
seq->last_value = DatumGetInt64(DirectFunctionCall1(int8in,
CStringGetDatum(values[i])));
else if (pg_strcasecmp(keys[i], "is_called") == 0)
seq->is_called = DatumGetBool(DirectFunctionCall1(boolin,
CStringGetDatum(values[i])));
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid state key \"%s\" for local sequence",
keys[i])));
}

sequence_save_tuple(seqh, NULL, true);

If that error happens after having already processed a "last_value" or
"is_called" entry, you have already modified the on-disk tuple. AFAICS
that's the only instance of that bug, but sequence_read_tuple - modify
tuple in place - sequence_save_tuple pattern is quite unsafe in general.
If you modify a tuple directly in a Buffer, you should have a critical
section around it. It would make sense to start a critical section in
sequence_read_tuple(), except that not all callers want to modify the
tuple in place. Perhaps the sequence_read_tuple/sequence_save_tuple
functions should be split into two:

sequence_read_tuple_for_update()
sequence_save_tuple()

* seqam_local_get_state() calls sequence_read_tuple() but not
sequence_release_tuple(). It looks like sequence_close() releases the
tuple and the buffer, but that seems sloppy. sequence_close() probably
shouldn't be calling sequence_release_tuple at all, so that you'd get a
warning at the end of transaction about the buffer leak.

- Heikki

Attachments:

seqam-v8+api-doc-heikki.patchapplication/x-patch; name=seqam-v8+api-doc-heikki.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index d0b78f2..150475e 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -249,6 +249,11 @@
      </row>
 
      <row>
+      <entry><link linkend="catalog-pg-seqam"><structname>pg_seqam</structname></link></entry>
+      <entry>sequence access methods</entry>
+     </row>
+
+     <row>
       <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
       <entry>dependencies on shared objects</entry>
      </row>
@@ -5512,6 +5517,95 @@
   </table>
  </sect1>
 
+ <sect1 id="catalog-pg-seqam">
+  <title><structname>pg_seqam</structname></title>
+
+  <indexterm zone="catalog-pg-seqam">
+   <primary>pg_seqam</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_seqam</structname> stores information about
+   sequence access methods. There is one row for each sequence access method
+   installed on the system.
+  </para>
+
+  <table>
+   <title><structname>pg_seqam</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+
+     <row>
+      <entry><structfield>oid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry></entry>
+      <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamreloptions</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to parse and validate <structfield>reloptions</> for the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqaminit</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function called during initialization or <command>RESET</command> of a sequence</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamalloc</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry><quote>Allocate new sequence id</quote> function</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetval</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function implementing <function>setval()</function> interface</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamgetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to dump current state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to restore a dumped state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="catalog-pg-shdepend">
   <title><structname>pg_shdepend</structname></title>
 
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 5095e0f..79053ac 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index e378d69..a3e8156 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -249,6 +249,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..8078fc7 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,6 +29,7 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
@@ -221,6 +222,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..c7609c6 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,7 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +224,17 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..3caab4d
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,247 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_seqam</structname> system catalog (see
+   <xref linkend="catalog-pg-seqam">).  The contents of a
+   <structname>pg_seqam</structname> row is the name of the access method
+   and are references to
+   <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>
+   entries that identify the functions provided by the access method.
+   functions supplied by the access method.
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The behaviour of a sequence access method is defined by a set of functions.
+   These functions are implemented by the sequence access method.
+  </para>
+
+   
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+void
+seqam_init (Oid seqrelid, List *seqparams, bytea *reloptions,
+            Datum *value, bool *nulls);
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, and when the sequence access method of an
+   existing sequence is changed by the <command>ALTER SEQUENCE</> command.
+  </para>
+
+  <para>
+   The <literal>seqrelid</> is InvalidOid if new sequence is being
+   initialized, otherwise it points to
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple representing the existing sequence.
+   <literal>seqparams</> is list of <structname>DefElem</>s corresponding
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+seqam_reloptions (ArrayType *reloptions, bool validate);
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value, which will be then sent
+   to the <function>seqam_init</> and stored in the catalog.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+seqam_alloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+             int64 *last);
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+bool
+seqam_setval Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+int
+seqam_get_state (Relation seqrel, SequenceHandle *seqh, char ***keys,
+                 char ***values);
+</programlisting>
+   Dump the current state of the sequence. Return value is the number of elements
+   in the output arrays <literal>keys</> and <literal>values</> which in turn
+   define the key/value pairs of the current state description. This interface
+   is mainly used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void
+seqam_set_state (Relation seqrel, SequenceHandle *seqh, char **keys,
+                 char **values, int count)
+</programlisting>
+   Restore state of the sequence based on the key/value pairs defined in
+   the <literal>keys</> and <literal>values</> parameters. The
+   <literal>count</> specifies length of the forementioned arrays.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid relid, SequenceHandle *seqh);
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh);
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+HeapTuple
+sequence_read_tuple (SequenceHandle *seqh);
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup,
+                    bool do_wal);
+</programlisting>
+   Save the modified sequence tuple. Note that the <literal>newtup</> can be
+   <symbol>NULL</> if the sequence tuple was modified inline.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh);
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_tuple</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh);
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_save_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors);
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value);
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_get_restart_value(List *options, int64 default_value,
+                           bool *found);
+</programlisting>
+   Given the list of <structname>DefElm</> <literal>options</> get the restart
+   value for a sequence. If it was not specified in the <literal>options</>
+   list the <literal>default_value</> will be returned and <literal>found</>
+   will be set to <literal>false</>.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..818da15 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+			  transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 8176b6a..9543503 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -829,7 +829,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -860,7 +861,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+			options = am_reloptions(amoptions, datum, false);
+			break;
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1308,14 +1312,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..f18e79b
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,301 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amdata.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAM data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_seqaminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Method API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_get_state		- dump sequence state (for pg_dump)
+ *		seqam_set_state		- restore sequence state (for pg_dump)
+ *
+ * Additionally, the am_reloptions interface is used for parsing reloptions
+ * that can be used for passing AM specific options.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+void
+seqam_init(Oid seqamid, Oid seqrelid, List *seqparams, List *reloptions,
+		   Datum *values, bool *nulls)
+{
+	FmgrInfo	procedure;
+	HeapTuple	tuple;
+	Form_pg_seqam seqamForm;
+	char	   *validnsps[2];
+	Datum       reloptions_transformed;
+	bytea	   *reloptions_parsed;
+
+	tuple = SearchSysCache1(SEQAMOID, seqamid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 seqamid);
+
+	seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+	fmgr_info(seqamForm->seqaminit, &procedure);
+
+	/* Allow am specific options */
+	validnsps[0] = NameStr(seqamForm->seqamname);
+	validnsps[1] = NULL;
+
+	/*
+	 * Parse AM-specific options, convert to text array form, retrieve the
+	 * AM-option function and then validate.
+	 */
+	reloptions_transformed = transformRelOptions((Datum) NULL, reloptions,
+												 NULL, validnsps, false,
+												 false);
+
+	reloptions_parsed = am_reloptions(seqamForm->seqamreloptions,
+									  reloptions_transformed, true);
+
+	/*
+	 * Have the seqam's proc do its work.
+	 */
+	FunctionCall5(&procedure,
+				  ObjectIdGetDatum(seqrelid),
+				  PointerGetDatum(seqparams),
+				  PointerGetDatum(reloptions_parsed),
+				  PointerGetDatum(values),
+				  PointerGetDatum(nulls));
+
+	ReleaseSysCache(tuple);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do its work.
+	 */
+	ret = FunctionCall4(procedure,
+						PointerGetDatum(seqrel),
+						PointerGetDatum(seqh),
+						Int64GetDatum(nrequested),
+						PointerGetDatum(last));
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	FunctionCall3(procedure,
+				  PointerGetDatum(seqrel),
+				  PointerGetDatum(seqh),
+				  Int64GetDatum(new_value));
+}
+
+/*
+ * seqam_get_state - pg_dump support
+ */
+int
+seqam_get_state(Relation seqrel, SequenceHandle *seqh, char ***keys,
+				char ***values)
+{
+	FmgrInfo	procedure;
+	Datum		count;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamgetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	count = FunctionCall4(&procedure,
+						  PointerGetDatum(seqrel),
+						  PointerGetDatum(seqh),
+						  PointerGetDatum(keys),
+						  PointerGetDatum(values));
+	return DatumGetInt32(count);
+}
+
+/*
+ * seqam_set_state - restore from pg_dump
+ */
+void
+seqam_set_state(Relation seqrel, SequenceHandle *seqh, char **keys,
+				char **values, int count)
+{
+	FmgrInfo	procedure;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamsetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	FunctionCall5(&procedure,
+				  PointerGetDatum(seqrel),
+				  PointerGetDatum(seqh),
+				  PointerGetDatum(keys),
+				  PointerGetDatum(values),
+				  Int32GetDatum(count));
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..2424b4a
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,337 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct LocalAmdata
+{
+	int64           log_cnt;
+} LocalAmdata;
+
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum		reloptions = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+	bytea	   *result;
+
+	result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+	if (result)
+		PG_RETURN_BYTEA_P(result);
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	Oid			seqrelid = PG_GETARG_OID(0);
+	List	   *seqoptions = (List *) PG_GETARG_POINTER(1);
+	Datum	   *values = (Datum *) PG_GETARG_POINTER(3);
+	bool	   *nulls = (bool *) PG_GETARG_POINTER(4);
+	bool		found_restart;
+	int64		start_value,
+				last_value,
+				min_value,
+				max_value;
+	LocalAmdata *localseq;
+
+	/* Get the new value to use as starting point. */
+	start_value = DatumGetInt64(values[SEQ_COL_STARTVAL - 1]);
+	start_value = sequence_get_restart_value(seqoptions, start_value,
+											 &found_restart);
+
+	/*
+	 * If this is new sequence or restart was provided, use starting value,
+	 * otherwise work with our saved value.
+	 */
+	if (seqrelid == InvalidOid || found_restart)
+		last_value = start_value;
+	else
+		last_value = DatumGetInt64(values[SEQ_COL_LASTVAL - 1]);
+
+	/* Validate the min/max against the starting point. */
+	min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+	max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+	sequence_check_range(last_value, min_value, max_value);
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (seqrelid == InvalidOid || found_restart)
+	{
+		nulls[SEQ_COL_LASTVAL - 1] = false;
+		nulls[SEQ_COL_CALLED - 1] = false;
+		values[SEQ_COL_LASTVAL - 1] = Int64GetDatum(last_value);
+		values[SEQ_COL_CALLED - 1] = BoolGetDatum(false);
+	}
+
+	if (nulls[SEQ_COL_AMDATA - 1])
+	{
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(LocalAmdata));
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(LocalAmdata));
+		nulls[SEQ_COL_AMDATA - 1] = false;
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+	}
+
+	localseq = (LocalAmdata *)
+		VARDATA_ANY(DatumGetByteaP(values[SEQ_COL_AMDATA - 1]));
+
+	/* We always reset the log_cnt. */
+	localseq->log_cnt = 0;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormData_pg_sequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	LocalAmdata *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+	next = result = seq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = seq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+		fetch = log = fetch + SEQ_LOG_VALS;
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 *
+	 * XXX: Does this need a critical section that would encapsulate both
+	 * changes?
+	 */
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		localseq->log_cnt = 0;
+
+		sequence_save_tuple(seqh, NULL, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = log;		/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, false);
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	int64		next = PG_GETARG_INT64(2);
+	FormData_pg_sequence *seq;
+	LocalAmdata *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = 0;		/* how much is logged */
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+Datum
+seqam_local_get_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	char	 ***out_keys = (char ***) PG_GETARG_POINTER(2);
+	char	 ***out_values = (char ***) PG_GETARG_POINTER(3);
+	char	  **keys;
+	char	  **values;
+	FormData_pg_sequence *seq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	keys = palloc(2 * sizeof(char *));
+	values = palloc(2 * sizeof(char *));
+
+	keys[0] = "last_value";
+	values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+										Int64GetDatum(seq->last_value)));
+
+	keys[1] = "is_called";
+	values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+										BoolGetDatum(seq->is_called)));
+
+	*out_keys = keys;
+	*out_values = values;
+
+	PG_RETURN_INT32(2);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+Datum
+seqam_local_set_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	char	  **keys = (char **) PG_GETARG_POINTER(2);
+	char	  **values = (char **) PG_GETARG_POINTER(3);
+	int			count = PG_GETARG_INT32(4);
+	FormData_pg_sequence *seq;
+	int			i;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	for (i = 0; i < count; i++)
+	{
+		if (pg_strcasecmp(keys[i], "last_value") == 0)
+			seq->last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+												CStringGetDatum(values[i])));
+		else if (pg_strcasecmp(keys[i], "is_called") == 0)
+			seq->is_called = DatumGetBool(DirectFunctionCall1(boolin,
+												CStringGetDatum(values[i])));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for local sequence",
+							keys[i])));
+	}
+
+	sequence_save_tuple(seqh, NULL, true);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a403c64..147c571 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 30cb699..a26d67d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 99acd4a..0c1cf58 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -527,7 +527,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 6d316d6..0cb2209 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,6 +14,9 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
@@ -22,8 +25,10 @@
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +41,13 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,6 +80,14 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTupleData tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -91,13 +98,12 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
 
 
 /*
@@ -112,6 +118,7 @@ DefineSequence(CreateSeqStmt *seq)
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
 	ObjectAddress address;
+	Oid			seqamid;
 	Relation	rel;
 	HeapTuple	tuple;
 	TupleDesc	tupDesc;
@@ -207,11 +214,6 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "cache_value";
 				value[i - 1] = Int64GetDatumFast(new.cache_value);
 				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
 			case SEQ_COL_CYCLE:
 				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
 				coldef->colname = "is_cycled";
@@ -222,6 +224,12 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "is_called";
 				value[i - 1] = BoolGetDatum(false);
 				break;
+			case SEQ_COL_AMDATA:
+				coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+				coldef->colname = "amdata";
+				null[i - 1] = true;
+				value[i - 1] = (Datum) 0;
+				break;
 		}
 		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
@@ -234,10 +242,26 @@ DefineSequence(CreateSeqStmt *seq)
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
 
+	/* Let AM fill the values[] and nulls[] for the tuple as well. */
+	if (seq->accessMethod)
+		seqamid = get_seqam_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+	seqam_init(seqamid, InvalidOid, seq->options, seq->amoptions,
+			   value, null);
+
 	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
+	/*
+	 * After we've created the sequence's relation in pg_class, update
+	 * the relam to a non-default value, if requested. We perform this
+	 * as a separate update to avoid invasive changes in normal code
+	 * paths and to keep the code similar between CREATE and ALTER.
+	 */
+	seqrel_update_relam(seqoid, seqamid);
+
 	rel = heap_open(seqoid, AccessExclusiveLock);
 	tupDesc = RelationGetDescr(rel);
 
@@ -254,6 +278,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +292,70 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	HeapTuple	newtup;
+	Relation	seqrel;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	SequenceHandle seqh;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
-	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	seqrel = seqh.rel;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+	memset(replaces, true, tupDesc->natts * sizeof(bool));
+
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+
+	seqam_init(seqrel->rd_rel->relam, seqrelid,
+			   list_make1(makeDefElem("restart", NULL)), NULL,
+			   values, nulls);
+
+	newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqh.rel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqh.rel, newtup);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
 }
 
 /*
@@ -361,7 +398,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/* check the comment above sequence_save_tuple()'s equivalent call. */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +412,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -406,19 +427,27 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
 	SeqTable	elm;
+	HeapTuple	tuple;
+	HeapTuple	newtup;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
 	ObjectAddress address;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	bool	   *replaces;
+	List	   *seqoptions;
+	SequenceHandle seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +455,113 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	seqoptions = stmt->options;
+	init_params(seqoptions, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	tupDesc = RelationGetDescr(seqrel);
+	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+	nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	oldamid = seqrel->rd_rel->relam;
+	if (stmt->accessMethod)
+		seqamid = get_seqam_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		bool	found;
 
-	MarkBufferDirty(buf);
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 *
+		 * This may not be the most beautiful way to do it but since
+		 * sequence AMs are expected to handle the RESTART option anyway,
+		 * it does not seem neccessary to invent special parameter for the
+		 * init API just for this.
+		 */
+		(void) sequence_get_restart_value(seqoptions, 0, &found);
+		if (!found)
+		{
+			DefElem	   *defel;
+			int64		last,
+						restart_value;
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+			restart_value = seqam_alloc(seqrel, &seqh, 1, &last);
+
+			defel = makeDefElem("restart", (Node *)makeInteger(restart_value));
+			seqoptions = lcons(defel, seqoptions);
+		}
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		/*
+		 * Create a new storage file for the sequence.
+		 * See ResetSequence for more info.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		/* Let the new sequence AM initialize. */
+		seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+				   values, nulls);
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* Rebuild the sequence tuple and save it. */
+		newtup = heap_form_tuple(tupDesc, values, nulls);
+		fill_seq_with_data(seqh.rel, newtup);
 
-		PageSetLSN(page, recptr);
+		/* Update the catalog. */
+		seqrel_update_relam(seqrelid, seqamid);
+	}
+	else
+	{
+		/* Let sequence AM update the tuple. */
+		replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+		memset(replaces, true, tupDesc->natts * sizeof(bool));
+		seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+				   values, nulls);
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
+		sequence_save_tuple(&seqh, newtup, true);
 	}
 
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	elm->cached = elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +602,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -570,193 +637,25 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
 	elm->cached = last;			/* last fetched number */
 	elm->last_valid = true;
+	elm->increment = seq_form->increment_by;
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for
-	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
-	 * no need to assign xids subxacts, that'll already trigger a appropriate
-	 * wait.  (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -766,28 +665,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -828,31 +726,24 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -862,92 +753,27 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	if (!seqrel->rd_islocaltemp)
 		PreventCommandIfReadOnly("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam_setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
-}
-
-/*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
+	last_used_seq = elm;
 
-	do_setval(relid, next, true);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
 /*
  * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
 setval3_oid(PG_FUNCTION_ARGS)
@@ -955,14 +781,61 @@ setval3_oid(PG_FUNCTION_ARGS)
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
 	bool		iscalled = PG_GETARG_BOOL(2);
+	char	   *keys[2];
+	char	   *values[2];
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
 
-	do_setval(relid, next, iscalled);
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
 
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-/*
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Convert the data into 'local' sequence dump format and call restore API. */
+	keys[0] = "last_value";
+	values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+													Int64GetDatum(next)));
+	keys[1] = "is_called";
+	values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+													BoolGetDatum(iscalled)));
+	seqam_set_state(seqh.rel, &seqh, keys, values, 2);
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
+
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
+
+	sequence_close(&seqh);
+
+	PG_RETURN_INT64(next);
+}
+
+/*
  * Open the sequence and acquire AccessShareLock if needed
  *
  * If we haven't touched the sequence already in this transaction,
@@ -1020,11 +893,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1076,44 +948,54 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	sequence_release_tuple(seqh);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1123,33 +1005,165 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
 	}
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	return &seqh->tup;
+}
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+/*
+ * Update a sequence tuple.
+ *
+ * If 'newtup' is valid, the tuple on the page is replaced with 'newtup'.
+ * Otherwise, the caller has modified the tuple directly on the page, and
+ * we just handle WAL-logging and dirtying the buffer here.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ */
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal)
+{
+	Page	page;
+
+	Assert(seqh->tup.t_data != NULL);
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+
+	/*
+	 * Force the change to be WAL-logged, if we the tuple hasn't been logged
+	 * since the last checkpoint.
+	 */
+	if (sequence_needs_wal(seqh))
+		do_wal = true;
+
+	/*
+	 * If something needs to be WAL logged, acquire an xid, so this
+	 * transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait.  (Have to do that here, so we're outside the critical section)
+	 */
+	if (do_wal && RelationNeedsWAL(seqh->rel))
+		GetTopTransactionId();
+
+	/*
+	 * If the caller passed a new tuple, replace the old one with it.
+	 * Otherwise just mark the buffer dirty and WAL-log it.
+	 */
+	if (HeapTupleIsValid(newtup))
+	{
+		Page	temppage;
+
+		/* Sequence tuples are always frozen. */
+		HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+		HeapTupleHeaderSetXminFrozen(newtup->t_data);
+		HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+		HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+		newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+		ItemPointerSet(&newtup->t_data->t_ctid, 0, FirstOffsetNumber);
+
+		/* Replace the original tuple on the page. */
+		temppage = PageGetTempPageCopySpecial(page);
+
+		if (PageAddItem(temppage, (Item) newtup->t_data, newtup->t_len,
+					FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+			elog(PANIC, "sequence_save_tuple: failed to add item to page");
+
+		PageSetLSN(temppage, PageGetLSN(page));
+
+		START_CRIT_SECTION();
+
+		PageRestoreTempPage(temppage, page);
+		seqh->tup.t_data = newtup->t_data;
+		seqh->tup.t_len = newtup->t_len;
+		MarkBufferDirtyHint(seqh->buf, true);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, newtup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
+	else
+	{
+		START_CRIT_SECTION();
+
+		MarkBufferDirtyHint(seqh->buf, true);
+
+		if (do_wal)
+			log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+
+		END_CRIT_SECTION();
+	}
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
+	{
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
+	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * sequence_save_tuple might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
+
+	Assert(BufferIsValid(seqh->buf));
+
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
+
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
+}
+
+/*
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1159,13 +1173,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1236,13 +1250,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1251,7 +1258,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1261,7 +1267,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1270,7 +1275,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1278,14 +1282,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1293,7 +1295,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1347,48 +1348,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1403,7 +1362,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1514,20 +1472,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1545,7 +1500,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1553,12 +1508,184 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *result;
+	int			count;
+	char	  **keys;
+	char	  **values;
+	int			i;
+	Datum	   *out_datums;
+	bool	   *out_nulls;
+	int			dims[1];
+	int			lbs[1];
+	SequenceHandle seqh;
+
+	sequence_open(relid, &seqh);
+
+	count = seqam_get_state(seqh.rel, &seqh, &keys, &values);
+
+	sequence_close(&seqh);
+
+	out_datums = palloc(count * 2 * sizeof(Datum));
+	out_nulls = palloc(count * 2 * sizeof(bool));
+
+	for (i = 0; i < count; i++)
+	{
+		text	   *key = cstring_to_text(keys[i]);
+
+		out_datums[i * 2] = PointerGetDatum(key);
+		out_nulls[i * 2] = false;
+
+		if (values[i] == NULL)
+		{
+			out_datums[i * 2 + 1] = (Datum) 0;
+			out_nulls[i * 2 + 1] = true;
+		}
+		else
+		{
+			text	   *value = cstring_to_text(values[i]);
+
+			out_datums[i * 2 + 1] = PointerGetDatum(value);
+			out_nulls[i * 2 + 1] = false;
+		}
+	}
+
+	dims[0] = count * 2;
+	lbs[0] = 1;
+
+	result = construct_md_array(out_datums, out_nulls,
+								1, dims, lbs,
+								TEXTOID, -1, false, 'i');
+
+	PG_RETURN_POINTER(result);
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *in_keyvals = PG_GETARG_ARRAYTYPE_P(1);
+	Datum	   *in_datums;
+	bool	   *in_nulls;
+	int			in_count;
+	int			count;
+	int			i;
+	char	  **keys;
+	char	  **values;
+	SequenceHandle seqh;
+
+	Assert(ARR_ELEMTYPE(in_keyvals) == TEXTOID);
+
+	/*
+	 * Do the input checks.
+	 */
+	if (ARR_NDIM(in_keyvals) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state must be one dimensional array")));
+
+	if ((ARR_DIMS(in_keyvals)[0]) % 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state array must have even number of elements")));
+
+	deconstruct_array(in_keyvals,
+					  TEXTOID, -1, false, 'i',
+					  &in_datums, &in_nulls, &in_count);
+
+	count = in_count / 2;
+	keys = palloc(count * sizeof(char *));
+	values = palloc(count * sizeof(char *));
+
+	for (i = 0; i < count; i++)
+	{
+		if (in_nulls[i * 2])
+			ereport(ERROR,
+					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+					 errmsg("null value not allowed for state array key")));
+
+		keys[i] = text_to_cstring(DatumGetTextP(in_datums[i * 2]));
+
+		if (in_nulls[i * 2 + 1])
+			values[i] = NULL;
+		else
+			values[i] = text_to_cstring(DatumGetTextP(in_datums[i * 2 + 1]));
+	}
+
+	/* Call in the sequence. */
+	sequence_open(relid, &seqh);
+
+	seqam_set_state(seqh.rel, &seqh, keys, values, count);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+	Relation	rd;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+
+	rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for sequence %u unavailable",
+						seqoid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	if (pgcform->relam != seqamid)
+	{
+		pgcform->relam = seqamid;
+		simple_heap_update(rd, &ctup->t_self, ctup);
+		CatalogUpdateIndexes(rd, ctup);
+	}
+
+	heap_freetuple(ctup);
+	heap_close(rd, RowExclusiveLock);
+	CommandCounterIncrement();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1624,3 +1751,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
+						bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
+					 bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 06e4332..a6d9996 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9240,7 +9240,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 029761e..16b3be7 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3371,7 +3371,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3384,7 +3386,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 190e50a..541ca60 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1563,7 +1563,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1574,7 +1576,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5818858..875a690 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3534,7 +3535,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3556,7 +3583,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3564,11 +3615,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3627,7 +3701,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 1fc8c2c..9832384 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -28,6 +28,7 @@
 
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -404,6 +405,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 1af43c6..ccb9791 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index e745006..aceb914 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1056,11 +1058,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1535,6 +1540,36 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	Form_pg_seqam	amform;
+
+	/*
+	 * Make a copy of the pg_seqam entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(CacheMemoryContext,
+												sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+
+	/*
+	 * Initialize sequence AM info struct. (It's left as zeroes, and filled
+	 * on-the-fly when used.)
+	 */
+	rel->rd_seqaminfo = (RelationSeqAmInfo *)
+		MemoryContextAllocZero(CacheMemoryContext,
+							   sizeof(RelationSeqAmInfo));
+}
 
 /*
  *		formrdesc
@@ -2021,6 +2056,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 		pfree(relation->rd_indextuple);
 	if (relation->rd_am)
 		pfree(relation->rd_am);
+	if (relation->rd_seqam)
+		pfree(relation->rd_seqam);
+	if (relation->rd_seqaminfo)
+		pfree(relation->rd_seqaminfo);
 	if (relation->rd_indexcxt)
 		MemoryContextDelete(relation->rd_indexcxt);
 	if (relation->rd_rulescxt)
@@ -4840,6 +4879,8 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqam == NULL);
+			Assert(rel->rd_seqaminfo == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index bd27168..5341a09 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAMNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_seqamname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAMOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f43aff2..8ab513d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index fe08c1b..ac93d1a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "libpq/libpq-fs.h"
@@ -4533,6 +4534,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4584,6 +4586,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4625,6 +4628,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4666,6 +4670,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4707,6 +4712,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4746,6 +4752,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4784,6 +4791,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4822,6 +4830,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4860,6 +4869,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4897,6 +4907,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4930,6 +4941,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4958,6 +4970,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4996,6 +5009,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5048,6 +5062,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5112,6 +5127,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -14534,7 +14553,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14614,6 +14634,37 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.5 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -14654,6 +14705,13 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14720,6 +14778,9 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -14736,16 +14797,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.5 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -14758,14 +14832,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index a9d3c10..7eaee32 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -221,6 +221,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_clas) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 04d769e..a01f319 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1401,30 +1401,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1468,13 +1444,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1803,6 +1821,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1812,12 +1832,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index e7b6bb5..4c28c8c 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -46,8 +46,9 @@ typedef enum relopt_kind
 	RELOPT_KIND_SPGIST = (1 << 8),
 	RELOPT_KIND_VIEW = (1 << 9),
 	RELOPT_KIND_BRIN = (1 << 10),
+	RELOPT_KIND_SEQUENCE = (1 << 11),
 	/* if you add a new kind, make sure you update "last_default" too */
-	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
+	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
 	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
 	RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -270,8 +271,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
-				 bool validate);
+extern bytea *am_reloptions(RegProcedure amoptions, Datum reloptions,
+						   bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..0ce2bd1
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+extern void seqam_init(Oid seqamid, Oid seqrelid, List *seqparams,
+					   List *reloptions, Datum *values, bool *nulls);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern int seqam_get_state(Relation seqrel, SequenceHandle *seqh,
+						   char ***keys, char ***values);
+void seqam_set_state(Relation seqrel, SequenceHandle *seqh,
+					 char **keys, char **values, int count);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup,
+								bool do_wal);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_local_set_state(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index a680229..09df8c3 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAMNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAMOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 619d996..07862e2 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1831,6 +1831,10 @@ 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 = 3284 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 1009 "2205" _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3285 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 1009" _null_ _null_ _null_ _null_ pg_sequence_set_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_ ));
 DESCR("I/O");
@@ -5119,6 +5123,20 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "26 2281 17 2281 2281" _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_get_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 4 0 23 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ seqam_local_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_set_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "2281 2281 2281 2281 23" _null_ _null_ _null_ _null_ seqam_local_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, is_temporary, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 DESCR("list objects dropped by the current command");
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..dec234a
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	3286
+
+CATALOG(pg_seqam,3286)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamgetstate;		/* dump state, used by pg_dump */
+	regproc		seqamsetstate;		/* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						7
+#define Anum_pg_seqam_seqamname				1
+#define Anum_pg_seqam_seqamreloptions		2
+#define Anum_pg_seqam_seqaminit				3
+#define Anum_pg_seqam_seqamalloc			4
+#define Anum_pg_seqam_seqamsetval			5
+#define Anum_pg_seqam_seqamgetstate			6
+#define Anum_pg_seqam_seqamsetstate			7
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 6022 (  local		seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_get_state seqam_local_set_state));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 44862bb..bed8d7d 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -30,9 +30,9 @@ typedef struct FormData_pg_sequence
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
 	bool		is_called;
+	bytea		amdata;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -48,12 +48,12 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 #define SEQ_COL_MAXVALUE		5
 #define SEQ_COL_MINVALUE		6
 #define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_CYCLE			8
+#define SEQ_COL_CALLED			9
+#define SEQ_COL_AMDATA			10
 
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_AMDATA
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +72,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0e257ac..44d6ea4 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2030,8 +2030,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2039,8 +2041,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 9e17d87..c61bc41 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -65,6 +66,18 @@ typedef struct RelationAmInfo
 
 
 /*
+ * Cached lookup information for the frequently used sequence access method
+ * functions, defined by the pg_seqam row associated with a sequencerelation.
+ */
+typedef struct RelationSeqAmInfo
+{
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+} RelationSeqAmInfo;
+
+
+/*
  * Here are the contents of a relation cache entry.
  */
 
@@ -125,6 +138,10 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	/* These are non-NULL only for a sequence relation */
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+	RelationSeqAmInfo *rd_seqaminfo; /* lookup info for funcs found in pg_seqam */
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
@@ -132,7 +149,7 @@ typedef struct RelationData
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index ba0b090..ac04502 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TSCONFIGMAP,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index c7be273..d4e250f 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3982a46 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | f         | \x0000000000000000
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | t         | \x1f00000000000000
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 9e7ba72..a69a56d 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -106,9 +106,9 @@ SELECT table_name, column_name, is_updatable
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
  ro_view19  | is_called     | NO
+ ro_view19  | amdata        | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
  ro_view20  | a             | NO
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..e1273ef 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
#73Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Heikki Linnakangas (#72)
Re: Sequence Access Method WIP

Heikki Linnakangas wrote:

* The transformations of the arrays in get_state() and set_state() functions
are a bit complicated. The seqam_get_state() function returns two C arrays,
and pg_sequence_get_state() turns them into a text[] array. Why not
construct the text[] array directly in the AM? I guess it's a bit more
convenient to the AM, if the pg_sequence_get_state() do that, but if that's
an issue, you could create generic helper function to turn two C string
arrays into text[], and call that from the AM.

Um, see strlist_to_textarray() in objectaddress.c if you do that. Maybe
we need some generic place to store that kind of helper function.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#74Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#72)
Re: Sequence Access Method WIP

On 20/04/15 17:50, Heikki Linnakangas wrote:

On 03/15/2015 09:07 PM, Petr Jelinek wrote:

Slightly updated version of the patch.

Mainly rebased against current master (there were several conflicts) and
fixed some typos, no real functional change.

I also attached initial version of the API sgml doc.

I finally got around to have another round of review on this. I fixed a
couple of little bugs, did some minor edition on comments etc. See
attached. It is also available in my git repository at
git://git.postgresql.org/git/users/heikki/postgres.git, branch "seqam",
if you want to look at individual changes. It combines your patches 1
and 4, I think those need to be applied together. I haven't looked at
the DDL changes yet.

Thanks!

I'm fairly happy with the alloc API now. I'm not sure it's a good idea
for the AM to access the sequence tuple directly, though. I would seem
cleaner if it only manipulated the amdata Datum. But it's not too bad as
it is.

Yeah, I was thinking about this myself I just liked sending 10
parameters to the function less than this.

The division of labour between sequence.c and the AM, in the init and
the get/set_state functions, is a bit more foggy:

* Do we really need a separate amoptions() method and an init() method,
when the only caller to amoptions() is just before the init() method?
The changes in extractRelOptions suggest that it would call
am_reloptions for sequences too, but that doesn't actually seem to be
happening.

Hmm yes it should and I am sure it did at some point, must have messed
it during one of the rebases :(

And it's the reason why we need separate API function.

postgres=# create sequence fooseq using local with (garbage=option);
CREATE SEQUENCE

Invalid options probably should throw an error.

* Currently, the AM's init function is responsible for basic sanity
checking, like min < max. It also has to extract the RESTART value from
the list of options. I think it would make more sense to move that to
sequence.c. The AM should only be responsible for initializing the
'amdata' portion, and for handling any AM-specific options. If the AM
doesn't support some options, like MIN and MAX value, it can check them
and throw an error, but it shouldn't be responsible for just passing
them through from the arguments to the sequence tuple.

Well then we need to send restart as additional parameter to the init
function as restart is normally not stored anywhere.

The checking is actually if new value is withing min/max but yes that
can be done inside sequence.c I guess.

* It might be better to form the sequence tuple before calling the init
function, and pass the ready-made tuple to it. All the other API
functions deal with the tuple (after calling sequence_read_tuple), so
that would be more consistent. The init function would need to
deconstruct it to modify it, but we're not concerned about performance
here.

Right, this is actually more of a relic of the custom columns
implementation where I didn't want to build the tuple with NULLs for
columns that might have been specified as NOT NULL, but with the amdata
approach it can create the tuple safely.

* The transformations of the arrays in get_state() and set_state()
functions are a bit complicated. The seqam_get_state() function returns
two C arrays, and pg_sequence_get_state() turns them into a text[]
array. Why not construct the text[] array directly in the AM? I guess
it's a bit more convenient to the AM, if the pg_sequence_get_state() do
that, but if that's an issue, you could create generic helper function
to turn two C string arrays into text[], and call that from the AM.

Yeah that was exactly the reasoning. Helper function works for me (will
check what Álvaro's suggested, maybe it can be moved somewhere and reused).

seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));

for (i = 0; i < count; i++)
{
if (pg_strcasecmp(keys[i], "last_value") == 0)
seq->last_value = DatumGetInt64(DirectFunctionCall1(int8in,

CStringGetDatum(values[i])));
else if (pg_strcasecmp(keys[i], "is_called") == 0)
seq->is_called = DatumGetBool(DirectFunctionCall1(boolin,

CStringGetDatum(values[i])));
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid state key \"%s\" for local
sequence",
keys[i])));
}

sequence_save_tuple(seqh, NULL, true);

If that error happens after having already processed a "last_value" or
"is_called" entry, you have already modified the on-disk tuple. AFAICS
that's the only instance of that bug, but sequence_read_tuple - modify
tuple in place - sequence_save_tuple pattern is quite unsafe in general.
If you modify a tuple directly in a Buffer, you should have a critical
section around it. It would make sense to start a critical section in
sequence_read_tuple(), except that not all callers want to modify the
tuple in place. Perhaps the sequence_read_tuple/sequence_save_tuple
functions should be split into two:

sequence_read_tuple_for_update()
sequence_save_tuple()

Agreed, however I am much more concerned about the way
seqam_local_alloc() works, see the XXX comment there. I am thinking it's
not really safe that way, but problem is that we can't put critical
section around it currently as the sequence_save_tuple() can potentially
call GetTopTransactionId(). Separating it into more functions might be
solution. Except the split into two does not really help there, it would
have to be something like this:
sequence_tuple_update_start() - does the GetTopTransactionId bit and
starts critical section
sequence_tuple_save() - saves the tuple/does wal
sequence_tuple_update_finish() - ends the critical section

That looks slightly cumbersome but I don't really have better idea.

* seqam_local_get_state() calls sequence_read_tuple() but not
sequence_release_tuple(). It looks like sequence_close() releases the
tuple and the buffer, but that seems sloppy. sequence_close() probably
shouldn't be calling sequence_release_tuple at all, so that you'd get a
warning at the end of transaction about the buffer leak.

Do you mean that you want to make call to sequence_release_tuple()
mandatory when sequence_read_tuple() was called?

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#75Petr Jelinek
petr@2ndquadrant.com
In reply to: Petr Jelinek (#74)
3 attachment(s)
Re: Sequence Access Method WIP

On 22/04/15 22:01, Petr Jelinek wrote:

On 20/04/15 17:50, Heikki Linnakangas wrote:

I finally got around to have another round of review on this. I fixed a
couple of little bugs, did some minor edition on comments etc. See
attached. It is also available in my git repository at
git://git.postgresql.org/git/users/heikki/postgres.git, branch "seqam",
if you want to look at individual changes. It combines your patches 1
and 4, I think those need to be applied together. I haven't looked at
the DDL changes yet.

Thanks!

I merged all your changes and merged the patch#1 with patch#4.

Also I pushed my repo to https://github.com/PJMODOS/postgres/tree/seqam
and gave you (Heikki) commit rights there in case you want to change
anything.

The division of labour between sequence.c and the AM, in the init and
the get/set_state functions, is a bit more foggy:

* Do we really need a separate amoptions() method and an init() method,
when the only caller to amoptions() is just before the init() method?
The changes in extractRelOptions suggest that it would call
am_reloptions for sequences too, but that doesn't actually seem to be
happening.

Hmm yes it should and I am sure it did at some point, must have messed
it during one of the rebases :(

And it's the reason why we need separate API function.

Actually the reloption handling was broken in more than one place, I
fixed it all around. I had to teach the heap_create_with_catalog about
relam but the change does not seem to be too intrusive to me so I think
it's fine. We no longer do the update of pg_class tuple after the
sequence was created anymore and the relcache works correctly now. Also
the whole reloption handling was moved to more proper places than the
seqam_init so it's more in line with how it works for other relation kinds.

postgres=# create sequence fooseq using local with (garbage=option);
CREATE SEQUENCE

Invalid options probably should throw an error.

Done, well actually the local sequence now throws error on any options.
I also updated CREATE SEQUENCE and ALTER SEQUENCE docs with the
reloptions syntax.

* Currently, the AM's init function is responsible for basic sanity
checking, like min < max. It also has to extract the RESTART value from
the list of options. I think it would make more sense to move that to
sequence.c. The AM should only be responsible for initializing the
'amdata' portion, and for handling any AM-specific options. If the AM
doesn't support some options, like MIN and MAX value, it can check them
and throw an error, but it shouldn't be responsible for just passing
them through from the arguments to the sequence tuple.

I now do the checking in sequence.c where possible, the init got
generally redone to accept the newly created tuple and restart value
parameters.

One thing that I am not sure how much like is that if the AM wants to
change the amdata in the init, it now has to modify the tuple and free
the old one.

* The transformations of the arrays in get_state() and set_state()
functions are a bit complicated. The seqam_get_state() function returns
two C arrays, and pg_sequence_get_state() turns them into a text[]
array. Why not construct the text[] array directly in the AM? I guess
it's a bit more convenient to the AM, if the pg_sequence_get_state() do
that, but if that's an issue, you could create generic helper function
to turn two C string arrays into text[], and call that from the AM.

Yeah that was exactly the reasoning. Helper function works for me (will
check what Álvaro's suggested, maybe it can be moved somewhere and reused).

Didn't use Álvaro's code in the end as ISTM working directly with arrays
is simple enough for this use case. The only slightly ugly part is the
use of TextDatumGetCString/CStringGetDatum but I think that's survivable
(maybe the sequence.c could convert the TEXT[] to CSTRING[] or just char
*[] as this is not performance critical code path).

seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));

for (i = 0; i < count; i++)
{
if (pg_strcasecmp(keys[i], "last_value") == 0)
seq->last_value = DatumGetInt64(DirectFunctionCall1(int8in,

CStringGetDatum(values[i])));
else if (pg_strcasecmp(keys[i], "is_called") == 0)
seq->is_called = DatumGetBool(DirectFunctionCall1(boolin,

CStringGetDatum(values[i])));
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid state key \"%s\" for local
sequence",
keys[i])));
}

sequence_save_tuple(seqh, NULL, true);

If that error happens after having already processed a "last_value" or
"is_called" entry, you have already modified the on-disk tuple. AFAICS
that's the only instance of that bug, but sequence_read_tuple - modify
tuple in place - sequence_save_tuple pattern is quite unsafe in general.
If you modify a tuple directly in a Buffer, you should have a critical
section around it. It would make sense to start a critical section in
sequence_read_tuple(), except that not all callers want to modify the
tuple in place. Perhaps the sequence_read_tuple/sequence_save_tuple
functions should be split into two:

sequence_read_tuple_for_update()
sequence_save_tuple()

Agreed, however I am much more concerned about the way
seqam_local_alloc() works, see the XXX comment there. I am thinking it's
not really safe that way, but problem is that we can't put critical
section around it currently as the sequence_save_tuple() can potentially
call GetTopTransactionId(). Separating it into more functions might be
solution. Except the split into two does not really help there, it would
have to be something like this:
sequence_tuple_update_start() - does the GetTopTransactionId bit and
starts critical section
sequence_tuple_save() - saves the tuple/does wal
sequence_tuple_update_finish() - ends the critical section

That looks slightly cumbersome but I don't really have better idea.

So I went with something like what was proposed:
- sequence_start_update(seqh, do_wal) - prepares the state and starts
the critical section
- sequence_apply_update(seqh, do_wal) - does buffer dirtying and wal logging
- sequence_finish_update(seqh) - closes the critical section and cleans
up the state

The API might look slightly hairy now but I don't see much difference
between having to do:
START_CRIT_SECTION()
sequence_update_tuple()
END_CRIT_SECTION()
and the above, except the above also solves the xid acquiring and shows
less internals.

I also added sequence_swap_tuple() for the use-case when the AM does not
want to do inline updates because having this separate makes things
easier and less ugly. And once you are changing whole tuple you are
already in slow/complex code path so one more function call does not matter.

* seqam_local_get_state() calls sequence_read_tuple() but not
sequence_release_tuple(). It looks like sequence_close() releases the
tuple and the buffer, but that seems sloppy. sequence_close() probably
shouldn't be calling sequence_release_tuple at all, so that you'd get a
warning at the end of transaction about the buffer leak.

I did this (requiring the sequence_release_tuple() always) and
documented it, but personally don't like it much as it just adds one
more call to the API which already has enough of them and I really don't
see the advantage of it.

Other than things mentioned above I rebased it on current master (the
transforms commit produced several issues). And fixed several bugs like
checking min/max value in set_state, proper initialization of session
cached increment_by, etc.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-seqam-v9.patchapplication/x-patch; name=0001-seqam-v9.patchDownload
From 33b883d88f79f5aafa5643931bd6d892344885cd Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Mon, 22 Sep 2014 19:22:40 +0200
Subject: [PATCH 1/3] seqam v9

---
 doc/src/sgml/catalogs.sgml                    |   94 ++
 doc/src/sgml/config.sgml                      |   21 +
 doc/src/sgml/filelist.sgml                    |    1 +
 doc/src/sgml/postgres.sgml                    |    1 +
 doc/src/sgml/ref/alter_sequence.sgml          |   55 ++
 doc/src/sgml/ref/create_sequence.sgml         |   25 +
 doc/src/sgml/seqam.sgml                       |  289 ++++++
 src/backend/access/Makefile                   |    3 +-
 src/backend/access/common/reloptions.c        |   12 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqam.c           |  308 ++++++
 src/backend/access/sequence/seqlocal.c        |  383 ++++++++
 src/backend/bootstrap/bootparse.y             |    1 +
 src/backend/catalog/Makefile                  |    2 +-
 src/backend/catalog/heap.c                    |   16 +-
 src/backend/catalog/objectaddress.c           |    1 +
 src/backend/catalog/toasting.c                |    1 +
 src/backend/commands/cluster.c                |    1 +
 src/backend/commands/createas.c               |    3 +-
 src/backend/commands/indexcmds.c              |    2 +-
 src/backend/commands/sequence.c               | 1245 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |   59 +-
 src/backend/commands/typecmds.c               |    3 +-
 src/backend/commands/view.c                   |    3 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   78 +-
 src/backend/parser/parse_utilcmd.c            |    3 +
 src/backend/tcop/utility.c                    |    6 +-
 src/backend/utils/cache/catcache.c            |    6 +-
 src/backend/utils/cache/relcache.c            |   60 +-
 src/backend/utils/cache/syscache.c            |   23 +
 src/backend/utils/misc/guc.c                  |   12 +
 src/backend/utils/misc/postgresql.conf.sample |    1 +
 src/bin/pg_dump/pg_dump.c                     |  115 ++-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/psql/describe.c                       |  112 ++-
 src/include/access/reloptions.h               |    4 +-
 src/include/access/seqam.h                    |   69 ++
 src/include/catalog/heap.h                    |    1 +
 src/include/catalog/indexing.h                |    5 +
 src/include/catalog/pg_proc.h                 |   18 +
 src/include/catalog/pg_seqam.h                |   74 ++
 src/include/commands/sequence.h               |   12 +-
 src/include/commands/tablecmds.h              |    2 +-
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/utils/guc.h                       |    1 +
 src/include/utils/rel.h                       |   19 +-
 src/include/utils/syscache.h                  |    2 +
 src/test/regress/expected/sanity_check.out    |    1 +
 src/test/regress/expected/sequence.out        |   35 +-
 src/test/regress/expected/updatable_views.out |    2 +-
 src/test/regress/sql/sequence.sql             |   10 +-
 53 files changed, 2635 insertions(+), 599 deletions(-)
 create mode 100644 doc/src/sgml/seqam.sgml
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqam.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqam.h
 create mode 100644 src/include/catalog/pg_seqam.h

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 898865e..403a223 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -249,6 +249,11 @@
      </row>
 
      <row>
+      <entry><link linkend="catalog-pg-seqam"><structname>pg_seqam</structname></link></entry>
+      <entry>sequence access methods</entry>
+     </row>
+
+     <row>
       <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
       <entry>dependencies on shared objects</entry>
      </row>
@@ -5536,6 +5541,95 @@
   </table>
  </sect1>
 
+ <sect1 id="catalog-pg-seqam">
+  <title><structname>pg_seqam</structname></title>
+
+  <indexterm zone="catalog-pg-seqam">
+   <primary>pg_seqam</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_seqam</structname> stores information about
+   sequence access methods. There is one row for each sequence access method
+   installed on the system.
+  </para>
+
+  <table>
+   <title><structname>pg_seqam</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+
+     <row>
+      <entry><structfield>oid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry></entry>
+      <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamreloptions</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to parse and validate <structfield>reloptions</> for the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqaminit</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function called during initialization or <command>RESET</command> of a sequence</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamalloc</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry><quote>Allocate new sequence id</quote> function</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetval</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function implementing <function>setval()</function> interface</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamgetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to dump current state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to restore a dumped state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="catalog-pg-shdepend">
   <title><structname>pg_shdepend</structname></title>
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b30c68d..ce40106 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5671,6 +5671,27 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-serial-sequenceam" xreflabel="serial_sequenceam">
+      <term><varname>serial_sequenceam</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>serial_sequenceam</> configuration parameter</primary>
+      </indexterm>
+      <indexterm><primary>sequence access method</><secondary>serial</></>
+      </term>
+      <listitem>
+       <para>
+        This variable specifies the default sequence access method to be used
+        for <type>SERIAL</> and <type>BIGSERIAL</>.
+       </para>
+
+       <para>
+        The default is 'local' sequence access method. If the value does not
+        match the name of any existing sequence access method, an error will be
+        raised.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-check-function-bodies" xreflabel="check_function_bodies">
       <term><varname>check_function_bodies</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 26aa7ee..5395229 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index e378d69..a3e8156 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -249,6 +249,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..a0aaa4b
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,289 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_seqam</structname> system catalog (see
+   <xref linkend="catalog-pg-seqam">).  The contents of a
+   <structname>pg_seqam</structname> row is the name of the access method
+   and are references to
+   <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>
+   entries that identify the functions provided by the access method.
+   functions supplied by the access method.
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The behaviour of a sequence access method is defined by a set of functions.
+   These functions are implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+HeapTuple
+seqam_init (Relation seqrel, HeapTuple tuple, int64 restart_value,
+            bool restart_requested, bool is_init);
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   <literal>tuple</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the updated
+   <structname>pg_sequence</structname>. If the tuple does not have to be
+   updated returning the input <literal>tuple</> is acceptable.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+seqam_reloptions (ArrayType *reloptions, bool validate);
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value, which will be then sent
+   to the <function>seqam_init</> and stored in the catalog.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+seqam_alloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+             int64 *last);
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+bool
+seqam_setval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+ArrayType *
+seqam_get_state (Relation seqrel, SequenceHandle *seqh);
+</programlisting>
+   Dump the current state of the sequence. The return value is one dimensional
+   <literal>TEXT</> array containing list of key/value pairs it the form
+   <literal>{'key1', 'value1', 'key2', 'value2'}</>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void
+seqam_set_state (Relation seqrel, SequenceHandle *seqh, ArrayType *statearr);
+</programlisting>
+   Restore state of the sequence based on the key/value pairs defined in
+   the <literal>statearr</> in <literal>{'key1', 'value1', 'key2', 'value2'}</>
+   form. The <literal>statearr</> is defined as <literal>TEXT[]</> in SQL. This
+   function must accept output of the <function>seqam_get_state()</> function.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid relid, SequenceHandle *seqh);
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh);
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+HeapTuple
+sequence_read_tuple (SequenceHandle *seqh);
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+<programlisting>
+HeapTuple
+sequence_swap_tuple (SequenceHandle *seqh, HeapTuple *newtup);
+</programlisting>
+   Changes the working tuple to the <literal>newtup</>. This does not change
+   the sequence itself, only the state of the <struct>SequenceHandle</>. To
+   save the tuple the <function>sequence_*_update()</> sequence has to be
+   called. The tuple should be released by calling
+   <function>sequence_release_tuple</>. Note that you can't call this function
+   without calling <function>sequence_read_tuple()</> first.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal);
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_apply_update(SequenceHandle *seqh, bool do_wal);
+</programlisting>
+   Save the modified sequence tuple indicating if the change should be WAL
+   logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh, HeapTuple newtuple);
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_apply_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh);
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_tuple</> and/or
+   added by <function>sequence_swap_tuple</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh);
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_apply_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors);
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname);
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..818da15 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+			  transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 8176b6a..9543503 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -829,7 +829,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -860,7 +861,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+			options = am_reloptions(amoptions, datum, false);
+			break;
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1308,14 +1312,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..161ec21
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,308 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amdata.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAM data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*serial_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_seqaminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Method API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_get_state		- dump sequence state (for pg_dump)
+ *		seqam_set_state		- restore sequence state (for pg_dump)
+ *
+ * Additionally, the am_reloptions interface is used for parsing reloptions
+ * that can be used for passing AM specific options.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+HeapTuple
+seqam_init(Relation seqrel, HeapTuple tuple, int64 restart_value,
+		   bool restart_requested, bool is_init)
+{
+	FmgrInfo	procedure;
+	Datum		ret;
+
+	fmgr_info(seqrel->rd_seqam->seqaminit, &procedure);
+
+	/*
+	 * Have the seqam's proc do its work.
+	 */
+	ret = FunctionCall5(&procedure,
+						PointerGetDatum(seqrel),
+						PointerGetDatum(tuple),
+						Int64GetDatum(restart_value),
+						BoolGetDatum(restart_requested),
+						BoolGetDatum(is_init));
+
+	return (HeapTuple) DatumGetPointer(ret);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do its work.
+	 */
+	ret = FunctionCall4(procedure,
+						PointerGetDatum(seqrel),
+						PointerGetDatum(seqh),
+						Int64GetDatum(nrequested),
+						PointerGetDatum(last));
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	FunctionCall3(procedure,
+				  PointerGetDatum(seqrel),
+				  PointerGetDatum(seqh),
+				  Int64GetDatum(new_value));
+}
+
+/*
+ * seqam_get_state - pg_dump support
+ */
+ArrayType *
+seqam_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	FmgrInfo	procedure;
+	Datum		statearr;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamgetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	statearr = FunctionCall2(&procedure,
+							 PointerGetDatum(seqrel),
+							 PointerGetDatum(seqh));
+
+	return (ArrayType *) DatumGetPointer(statearr);
+}
+
+/*
+ * seqam_set_state - restore from pg_dump
+ */
+void
+seqam_set_state(Relation seqrel, SequenceHandle *seqh, ArrayType *statearr)
+{
+	FmgrInfo	procedure;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamsetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	FunctionCall3(&procedure,
+				  PointerGetDatum(seqrel),
+				  PointerGetDatum(seqh),
+				  PointerGetDatum(statearr));
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new serial_seqam value */
+bool
+check_serial_seqam(char **newval, void **extra, GucSource source)
+{
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.  Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence access method \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence access method \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..a891bd8
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,383 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct LocalAmdata
+{
+	int64           log_cnt;
+} LocalAmdata;
+
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum		reloptions = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	HeapTuple	tuple = (HeapTuple) PG_GETARG_POINTER(1);
+	int64		restart_value = PG_GETARG_INT64(2);
+	bool		restart_requested = PG_GETARG_BOOL(3);
+	bool		is_init = PG_GETARG_BOOL(4);
+	bool		isnull;
+	Datum		amdata;
+	TupleDesc	tupDesc = RelationGetDescr(seqrel);
+	Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(tuple);
+	LocalAmdata *localseq;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seq->last_value = restart_value;
+		seq->is_called = false;
+	}
+
+	amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+						 &isnull);
+
+	/* Make sure amdata is filled. */
+	if (isnull)
+	{
+		Datum			values[SEQ_COL_LASTCOL];
+		bool			nulls[SEQ_COL_LASTCOL];
+		bool			replace[SEQ_COL_LASTCOL];
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(LocalAmdata));
+		HeapTuple		newtup;
+
+		memset(replace, false, sizeof(replace));
+		memset(nulls, false, sizeof(nulls));
+		memset(values, 0, sizeof(values));
+
+		replace[SEQ_COL_AMDATA - 1] = true;
+		nulls[SEQ_COL_AMDATA - 1] = false;
+
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(LocalAmdata));
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replace);
+
+		/* Don't leak memory. */
+		heap_freetuple(tuple);
+		tuple = newtup;
+
+		amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+							 &isnull);
+	}
+
+	localseq = (LocalAmdata *)
+		VARDATA_ANY(DatumGetByteaP(amdata));
+
+	/* We always reset the log_cnt. */
+	localseq->log_cnt = 0;
+
+	PG_RETURN_POINTER(tuple);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormData_pg_sequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	LocalAmdata *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+	next = result = seq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = seq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	sequence_start_update(seqh, logit);
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		localseq->log_cnt = 0;
+
+		sequence_apply_update(seqh, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = log;		/* how much is logged */
+
+	sequence_apply_update(seqh, false);
+	sequence_finish_update(seqh);
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	int64		next = PG_GETARG_INT64(2);
+	FormData_pg_sequence *seq;
+	LocalAmdata *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = 0;		/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_apply_update(seqh, true);
+	sequence_finish_update(seqh);
+	sequence_release_tuple(seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+Datum
+seqam_local_get_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	Datum			datums[4];
+	Datum			val;
+	FormData_pg_sequence *seq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	datums[0] = CStringGetTextDatum("last_value");
+	val = DirectFunctionCall1(int8out, Int64GetDatum(seq->last_value));
+	datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+	datums[2] = CStringGetTextDatum("is_called");
+	val = DirectFunctionCall1(boolout, BoolGetDatum(seq->is_called));
+	datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+	sequence_release_tuple(seqh);
+
+	PG_RETURN_ARRAYTYPE_P(construct_array(datums, 4, TEXTOID, -1, false, 'i'));
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+Datum
+seqam_local_set_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	ArrayType	   *statearr = PG_GETARG_ARRAYTYPE_P(2);
+	FormData_pg_sequence *seq;
+	int64			last_value = 0;
+	bool			is_called = false;
+	bool			last_value_found = false,
+					is_called_found = false;
+	Datum		   *datums;
+	int				count;
+	int				i;
+
+	Assert(ARR_ELEMTYPE(statearr) == TEXTOID && ARR_NDIM(statearr) == 1 &&
+		   (ARR_DIMS(statearr)[0]) % 2 == 0 && !ARR_HASNULL(statearr));
+
+	deconstruct_array(statearr,
+					  TEXTOID, -1, false, 'i',
+					  &datums, NULL, &count);
+	count /= 2;
+	for (i = 0; i < count; ++i)
+	{
+		char   *key,
+			   *val;
+
+		key = TextDatumGetCString(datums[i * 2]);
+		val = TextDatumGetCString(datums[i * 2 + 1]);
+
+		if (pg_strcasecmp(key, "last_value") == 0)
+		{
+			last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+														CStringGetDatum(val)));
+			last_value_found = true;
+		}
+		else if (pg_strcasecmp(key, "is_called") == 0)
+		{
+			is_called = DatumGetBool(DirectFunctionCall1(boolin,
+														CStringGetDatum(val)));
+			is_called_found = true;
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for local sequence",
+							key)));
+	}
+
+	if (!last_value_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("last_value is required parameter for local sequence")));
+
+	if (!is_called_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("is_called is required parameter for local sequence")));
+
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	sequence_check_range(last_value, seq->min_value, seq->max_value, "last_value");
+
+	sequence_start_update(seqh, true);
+	seq->last_value = last_value;
+	seq->is_called = is_called;
+	sequence_apply_update(seqh, true);
+	sequence_finish_update(seqh);
+	sequence_release_tuple(seqh);
+
+	PG_FREE_IF_COPY(statearr, 2);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 6e563b6..1c85569 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -252,6 +252,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index c73f20d..4fd8588 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d04e94d..1fd46a6 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -87,6 +87,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -847,6 +848,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -920,6 +922,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1033,6 +1036,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1261,6 +1265,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1273,7 +1278,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1328,6 +1334,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = SeqAccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 10f0396..52d1d59 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index c99d353..0b35046 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -290,6 +290,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 3febdd5..e2c1990 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -678,6 +678,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 54b2f38..7d4133b 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 351d48e..bcf1065 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -527,7 +527,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 6d316d6..060b3c4 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,6 +14,9 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
@@ -22,8 +25,10 @@
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +41,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,6 +81,16 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTupleData tup;
+	Page		temppage;
+	bool		inupdate;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -91,13 +101,11 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
 
 
 /*
@@ -111,6 +119,7 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
@@ -147,6 +156,11 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
+	if (seq->accessMethod)
+		seqamid = get_seqam_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
 	/*
 	 * Create relation (and fill value[] and null[] for the tuple)
 	 */
@@ -207,11 +221,6 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "cache_value";
 				value[i - 1] = Int64GetDatumFast(new.cache_value);
 				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
 			case SEQ_COL_CYCLE:
 				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
 				coldef->colname = "is_cycled";
@@ -222,6 +231,12 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "is_called";
 				value[i - 1] = BoolGetDatum(false);
 				break;
+			case SEQ_COL_AMDATA:
+				coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+				coldef->colname = "amdata";
+				null[i - 1] = true;
+				value[i - 1] = (Datum) 0;
+				break;
 		}
 		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
@@ -229,12 +244,13 @@ DefineSequence(CreateSeqStmt *seq)
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
@@ -243,6 +259,7 @@ DefineSequence(CreateSeqStmt *seq)
 
 	/* now initialize the sequence's data */
 	tuple = heap_form_tuple(tupDesc, value, null);
+	tuple = seqam_init(rel, tuple, new.start_value, false, true);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +271,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +285,55 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tuple = seqam_init(seqrel, tuple, seq->start_value, true, false);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
 }
 
 /*
@@ -361,7 +376,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +396,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -406,19 +411,23 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
 	ObjectAddress address;
+	List	   *seqoptions;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +435,173 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	seqoptions = stmt->options;
+	init_params(seqoptions, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	oldamid = seqrel->rd_rel->relam;
+	if (stmt->accessMethod)
+		seqamid = get_seqam_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(seqoptions, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		Form_pg_seqam	seqam;
+		HeapTuple       seqamtup;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = seqam_alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		seqam = (Form_pg_seqam) GETSTRUCT(seqamtup);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		validnsps[0] = NameStr(seqam->seqamname);
+		validnsps[1] = NULL;
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		PageSetLSN(page, recptr);
-	}
+		(void) am_reloptions(seqam->seqamreloptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-	END_CRIT_SECTION();
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-	UnlockReleaseBuffer(buf);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
+
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
+
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/* Let the new sequence AM initialize. */
+		tuple = seqam_init(seqrel, tuple, restart_value, true, true);
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * See ResetSequence for why we do this.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		/*
+		 * Insert the modified tuple into the new storage file.
+		 */
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										SeqAccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = SeqAccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+	else
+	{
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
+
+		/* Let sequence AM update the tuple. */
+		tuple = seqam_init(seqrel, tuple, restart_value, restart_requested, false);
+		sequence_swap_tuple(&seqh, tuple);
+		sequence_start_update(&seqh, true);
+		sequence_apply_update(&seqh, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +642,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -570,121 +677,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -693,70 +694,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for
-	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
-	 * no need to assign xids subxacts, that'll already trigger a appropriate
-	 * wait.  (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -766,28 +705,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -828,31 +766,24 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -862,92 +793,27 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	if (!seqrel->rd_islocaltemp)
 		PreventCommandIfReadOnly("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seqam_setval(seqrel, &seqh, next);
 
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
-
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
-}
-
-/*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
+	last_used_seq = elm;
 
-	do_setval(relid, next, true);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
 /*
  * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
 setval3_oid(PG_FUNCTION_ARGS)
@@ -955,13 +821,67 @@ setval3_oid(PG_FUNCTION_ARGS)
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
 	bool		iscalled = PG_GETARG_BOOL(2);
+	ArrayType  *statearr;
+	Datum		datums[4];
+	Datum		val;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
+
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Convert the data into 'local' sequence dump format and call restore API. */
+	datums[0] = CStringGetTextDatum("last_value");
+	val = DirectFunctionCall1(int8out, Int64GetDatum(next));
+	datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+	datums[2] = CStringGetTextDatum("is_called");
+	val = DirectFunctionCall1(boolout, BoolGetDatum(iscalled));
+	datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+	statearr = construct_array(datums, 4, TEXTOID, -1, false, 'i');
+
+	seqam_set_state(seqh.rel, &seqh, statearr);
+
+	pfree(statearr);
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
+
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1020,11 +940,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1076,44 +995,56 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->temppage = NULL;
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(seqh->temppage == NULL && !seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
 
-	page = BufferGetPage(*buf);
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1123,33 +1054,185 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+/*
+ * Swap the current working tuple.
+ *
+ * Note that the changes are only saved to in-memory state and will not be
+ * visible unless the sequence_*_update sequence is called.
+ */
+void
+sequence_swap_tuple(SequenceHandle *seqh, HeapTuple newtup)
+{
+	Page	page;
+
+	Assert(!seqh->inupdate && seqh->tup.t_data != NULL &&
+		   seqh->temppage == NULL);
+
+	page = BufferGetPage(seqh->buf);
+
+	seqh->temppage = PageGetTempPageCopySpecial(page);
+
+	/* Sequence tuples are always frozen. */
+	HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+	HeapTupleHeaderSetXminFrozen(newtup->t_data);
+	HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+	HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+	newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+	ItemPointerSet(&newtup->t_data->t_ctid, 0, FirstOffsetNumber);
+
+	if (PageAddItem(seqh->temppage, (Item) newtup->t_data, newtup->t_len,
+				FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+		elog(PANIC, "sequence_apply_update: failed to add item to page");
+
+	PageSetLSN(seqh->temppage, PageGetLSN(page));
+
+	seqh->tup.t_data = newtup->t_data;
+	seqh->tup.t_len = newtup->t_len;
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+	if (do_wal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_apply_update(SequenceHandle *seqh, bool do_wal)
+{
+	Page	page;
+
+	Assert(seqh->inupdate && seqh->tup.t_data != NULL);
+
+	page = BufferGetPage(seqh->buf);
+
+	/*
+	 * If the working tuple was swapped we need to copy it to the page
+	 */
+	if (seqh->temppage != NULL)
+	{
+		PageRestoreTempPage(seqh->temppage, page);
+		seqh->temppage = NULL;
+	}
+
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (do_wal)
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate && seqh->temppage == NULL);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	if (seqh->temppage)
+	{
+		pfree(seqh->temppage);
+		seqh->temppage = NULL;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
+
+	Assert(BufferIsValid(seqh->buf));
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
 
-	return seq;
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
+ *
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1159,13 +1242,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1236,13 +1319,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1251,7 +1327,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1261,7 +1336,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1270,7 +1344,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1278,14 +1351,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1293,7 +1364,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1347,48 +1417,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1403,7 +1431,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1514,20 +1541,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1545,7 +1569,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1553,12 +1577,89 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *statearr;
+	SequenceHandle seqh;
+
+	sequence_open(relid, &seqh);
+
+	statearr = seqam_get_state(seqh.rel, &seqh);
+
+	sequence_close(&seqh);
+
+	Assert(ARR_ELEMTYPE(statearr) == TEXTOID && ARR_NDIM(statearr) == 1 &&
+		   (ARR_DIMS(statearr)[0]) % 2 == 0 && !ARR_HASNULL(statearr));
+
+	PG_RETURN_ARRAYTYPE_P(statearr);
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *statearr = PG_GETARG_ARRAYTYPE_P(1);
+	SequenceHandle seqh;
+
+	Assert(ARR_ELEMTYPE(statearr) == TEXTOID);
+
+	/*
+	 * Do the input checks.
+	 */
+	if (ARR_NDIM(statearr) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state must be one dimensional array")));
+
+	if ((ARR_DIMS(statearr)[0]) % 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state array must have even number of elements")));
+
+	if (array_contains_nulls(statearr))
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state array cannot contain NULLs")));
+
+	/* Call in the sequence. */
+	sequence_open(relid, &seqh);
+
+	seqam_set_state(seqh.rel, &seqh, statearr);
+
+	sequence_close(&seqh);
+
+	PG_FREE_IF_COPY(statearr, 1);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1624,3 +1725,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index bedd8ae..dcc9040 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -266,6 +266,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -446,7 +447,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -465,7 +466,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -543,13 +543,43 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		HeapTuple	tuple;
+		Form_pg_seqam seqam;
+		static char *validnsps[2];
+
+		Assert(relamid != InvalidOid);
+
+		tuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(relamid));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 relamid);
+
+		seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+
+		validnsps[0] = NameStr(seqam->seqamname);
+		validnsps[1] = NULL;
+
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+
+		(void) am_reloptions(seqam->seqamreloptions, reloptions, true);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		ReleaseSysCache(tuple);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -670,6 +700,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3296,7 +3327,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4279,6 +4310,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4319,8 +4353,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW:
 			msg = _("\"%s\" is not a table or view");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9248,7 +9282,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqam->seqamreloptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index ab13be2..a69ad61 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2112,7 +2112,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index efa4be1..3cea360 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1685efe..314f7e1 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3372,7 +3372,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3385,7 +3387,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 578ead5..7c0a51e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1563,7 +1563,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1574,7 +1576,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 962a69d..500c05e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3538,7 +3539,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3560,7 +3587,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3568,11 +3619,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3631,7 +3705,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 73924ae..c68b773 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -28,6 +28,7 @@
 
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -415,6 +416,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = serial_seqam;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 31e9d4c..e726831 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -932,7 +932,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 
 							/*
 							 * Let NewRelationCreateToastTable decide if this
@@ -962,7 +963,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 						}
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 1af43c6..ccb9791 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index e745006..168bee7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -429,6 +431,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	Oid			amoptions = InvalidOid;
 
 	relation->rd_options = NULL;
 
@@ -437,10 +440,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_am->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqam->seqamreloptions;
+			break;
 		default:
 			return;
 	}
@@ -452,8 +460,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_am->amoptions : InvalidOid);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1056,11 +1063,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1535,6 +1545,36 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	Form_pg_seqam	amform;
+
+	/*
+	 * Make a copy of the pg_seqam entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(CacheMemoryContext,
+												sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+
+	/*
+	 * Initialize sequence AM info struct. (It's left as zeroes, and filled
+	 * on-the-fly when used.)
+	 */
+	rel->rd_seqaminfo = (RelationSeqAmInfo *)
+		MemoryContextAllocZero(CacheMemoryContext,
+							   sizeof(RelationSeqAmInfo));
+}
 
 /*
  *		formrdesc
@@ -2021,6 +2061,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 		pfree(relation->rd_indextuple);
 	if (relation->rd_am)
 		pfree(relation->rd_am);
+	if (relation->rd_seqam)
+		pfree(relation->rd_seqam);
+	if (relation->rd_seqaminfo)
+		pfree(relation->rd_seqaminfo);
 	if (relation->rd_indexcxt)
 		MemoryContextDelete(relation->rd_indexcxt);
 	if (relation->rd_rulescxt)
@@ -4840,6 +4884,8 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqam == NULL);
+			Assert(rel->rd_seqaminfo == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 644bbcc..38f0d35 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_transform.h"
@@ -632,6 +633,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAMNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_seqamname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAMOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f43aff2..1d31cea 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2894,6 +2895,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"serial_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence access method for SERIAL and BIGSERIAL column types."),
+			gettext_noop("Defaults to 'local' sequence access method."),
+			GUC_IS_NAME
+		},
+		&serial_seqam,
+		"local",
+		check_serial_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 110983f..a90637b 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -512,6 +512,7 @@
 #default_tablespace = ''		# a tablespace name, '' uses the default
 #temp_tablespaces = ''			# a list of tablespace names, '' uses
 					# only default tablespace
+#serial_sequenceam = 'local'	# default sequence access method for SERIAL
 #check_function_bodies = on
 #default_transaction_isolation = 'read committed'
 #default_transaction_read_only = off
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 977b72e..573b1de 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "libpq/libpq-fs.h"
@@ -4534,6 +4535,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4585,6 +4587,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4626,6 +4629,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4667,6 +4671,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4708,6 +4713,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4747,6 +4753,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4785,6 +4792,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4823,6 +4831,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4861,6 +4870,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4898,6 +4908,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4931,6 +4942,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4959,6 +4971,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4997,6 +5010,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5049,6 +5063,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5113,6 +5128,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -14802,7 +14821,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14882,6 +14902,37 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.5 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -14922,6 +14973,13 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14988,6 +15046,9 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15004,16 +15065,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.5 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15026,14 +15100,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 4c796ad..3d2b66a 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -222,6 +222,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_clas) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 04d769e..a01f319 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1401,30 +1401,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1468,13 +1444,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1803,6 +1821,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1812,12 +1832,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index e7b6bb5..7523973 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -270,8 +270,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
-				 bool validate);
+extern bytea *am_reloptions(RegProcedure amoptions, Datum reloptions,
+						   bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..5040118
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,69 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+extern char *serial_seqam;
+
+extern HeapTuple seqam_init(Relation seqrel, HeapTuple tuple,
+							int64 restart_value, bool restart_requested,
+							bool is_init);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern ArrayType * seqam_get_state(Relation seqrel, SequenceHandle *seqh);
+extern void seqam_set_state(Relation seqrel, SequenceHandle *seqh,
+					 ArrayType *statearr);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_swap_tuple(SequenceHandle *seqh, HeapTuple newtup);
+extern void sequence_start_update(SequenceHandle *seqh, bool do_wal);
+extern void sequence_apply_update(SequenceHandle *seqh, bool do_wal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_local_set_state(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index e6ac394..ce0ba57 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index a234bde..e4c634f 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAMNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAMOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e97e6b1..aacd906 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1833,6 +1833,10 @@ 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 1009 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 1009" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -5121,6 +5125,20 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 17 "2281 2281 20 16 16" _null_ _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_get_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 1009 "2281 2281" _null_ _null_ _null_ _null_ _null_ seqam_local_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_set_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 1009" _null_ _null_ _null_ _null_ _null_ seqam_local_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, is_temporary, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 DESCR("list objects dropped by the current command");
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..dec234a
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	3286
+
+CATALOG(pg_seqam,3286)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamgetstate;		/* dump state, used by pg_dump */
+	regproc		seqamsetstate;		/* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						7
+#define Anum_pg_seqam_seqamname				1
+#define Anum_pg_seqam_seqamreloptions		2
+#define Anum_pg_seqam_seqaminit				3
+#define Anum_pg_seqam_seqamalloc			4
+#define Anum_pg_seqam_seqamsetval			5
+#define Anum_pg_seqam_seqamgetstate			6
+#define Anum_pg_seqam_seqamsetstate			7
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 6022 (  local		seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_get_state seqam_local_set_state));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 44862bb..bed8d7d 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -30,9 +30,9 @@ typedef struct FormData_pg_sequence
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
 	bool		is_called;
+	bytea		amdata;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -48,12 +48,12 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 #define SEQ_COL_MAXVALUE		5
 #define SEQ_COL_MINVALUE		6
 #define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_CYCLE			8
+#define SEQ_COL_CALLED			9
+#define SEQ_COL_AMDATA			10
 
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_AMDATA
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +72,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index f269c63..cb621b1 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 852eb4f..c55c9de 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2040,8 +2040,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2049,8 +2051,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ff78b70..1ac3ef7 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -410,6 +410,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_serial_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 9e17d87..c61bc41 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -65,6 +66,18 @@ typedef struct RelationAmInfo
 
 
 /*
+ * Cached lookup information for the frequently used sequence access method
+ * functions, defined by the pg_seqam row associated with a sequencerelation.
+ */
+typedef struct RelationSeqAmInfo
+{
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+} RelationSeqAmInfo;
+
+
+/*
  * Here are the contents of a relation cache entry.
  */
 
@@ -125,6 +138,10 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	/* These are non-NULL only for a sequence relation */
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+	RelationSeqAmInfo *rd_seqaminfo; /* lookup info for funcs found in pg_seqam */
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
@@ -132,7 +149,7 @@ typedef struct RelationData
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index ff9a4f2..e7a4224 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TRFOID,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 52c9e77..b9308f6 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3dc5af9 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | f         | \x0000000000000000
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | t         | \x1f00000000000000
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index ccabe9e..34faedd 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -106,9 +106,9 @@ SELECT table_name, column_name, is_updatable
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
  ro_view19  | is_called     | NO
+ ro_view19  | amdata        | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
  ro_view20  | a             | NO
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..0a54569 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
-- 
1.9.1

0002-seqam-ddl-v5.patchapplication/x-patch; name=0002-seqam-ddl-v5.patchDownload
From 05fda235c599d8d88c7bf4d535a1c15cc605b3e8 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Sun, 15 Feb 2015 03:00:24 +0100
Subject: [PATCH 2/3] seqam ddl v5

---
 doc/src/sgml/catalogs.sgml           |   6 +
 doc/src/sgml/ref/allfiles.sgml       |   2 +
 doc/src/sgml/ref/create_seqam.sgml   | 163 ++++++++++++++++++
 doc/src/sgml/ref/drop_seqam.sgml     |  87 ++++++++++
 doc/src/sgml/reference.sgml          |   2 +
 src/backend/access/sequence/seqam.c  | 308 +++++++++++++++++++++++++++++++++++
 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/parser/gram.y            |  13 +-
 src/backend/tcop/utility.c           |  12 ++
 src/bin/pg_dump/common.c             |   5 +
 src/bin/pg_dump/pg_dump.c            | 158 +++++++++++++++++-
 src/bin/pg_dump/pg_dump.h            |   9 +-
 src/bin/pg_dump/pg_dump_sort.c       |  11 +-
 src/include/access/seqam.h           |   3 +
 src/include/catalog/dependency.h     |   1 +
 src/include/nodes/parsenodes.h       |   1 +
 src/include/parser/kwlist.h          |   1 +
 20 files changed, 858 insertions(+), 8 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/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 403a223..f70ea17 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -5554,6 +5554,12 @@
    installed on the system.
   </para>
 
+  <para>
+   The contents of this catalog can be manipulated by
+   <command>CREATE ACCESS METHOD FOR SEQUENCES</> and
+   <command>DROP ACCESS METHOD FOR SEQUENCES</>.
+  </para>
+
   <table>
    <title><structname>pg_seqam</> Columns</title>
 
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index bf95453..2fef865 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">
@@ -117,6 +118,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..e95409f
--- /dev/null
+++ b/doc/src/sgml/ref/create_seqam.sgml
@@ -0,0 +1,163 @@
+<!--
+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> (
+    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> ,
+    GETSTATE = <replaceable class="parameter">getstate_function</replaceable>,
+    SETSTATE = <replaceable class="parameter">setstate_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">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">getstate_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">setstate_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 03020df..35c0c05 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -102,6 +102,7 @@
    &createRole;
    &createRule;
    &createSchema;
+   &createSeqAM;
    &createSequence;
    &createServer;
    &createTable;
@@ -145,6 +146,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 161ec21..e2ecdab 100644
--- a/src/backend/access/sequence/seqam.c
+++ b/src/backend/access/sequence/seqam.c
@@ -67,8 +67,20 @@
 #include "access/seqam.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"
 
@@ -276,6 +288,302 @@ check_serial_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[1] = INTERNALOID;
+			typeId[2] = INT8OID;
+			typeId[3] = BOOLOID;
+			typeId[4] = BOOLOID;
+			retTypeId = BYTEAOID;
+			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_seqamgetstate:
+			nargs = 2;
+			typeId[1] = INTERNALOID;
+			retTypeId = TEXTARRAYOID;
+			break;
+
+		case Anum_pg_seqam_seqamsetstate:
+			nargs = 3;
+			typeId[1] = INTERNALOID;
+			typeId[2] = TEXTARRAYOID;
+			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);
+}
+
+
+/*
+ * make pg_depend entries for a new pg_seqam entry
+ */
+static void
+makeSeqAMDependencies(HeapTuple tuple)
+{
+	Form_pg_seqam	seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+	ObjectAddress	myself,
+					referenced;
+
+
+	myself.classId = SeqAccessMethodRelationId;
+	myself.objectId = HeapTupleGetOid(tuple);
+	myself.objectSubId = 0;
+
+	/* Dependency on extension. */
+	recordDependencyOnCurrentExtension(&myself, false);
+
+	/* 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->seqamgetstate;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.objectId = seqam->seqamsetstate;
+	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* definition)
+{
+	char	   *seqamname = strVal(linitial(names));
+	Oid			seqamoid;
+	ListCell   *pl;
+	Relation	rel;
+	Datum		values[Natts_pg_seqam];
+	bool		nulls[Natts_pg_seqam];
+	HeapTuple	tuple;
+
+	/* 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));
+
+	/*
+	 * 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, "getstate") == 0)
+		{
+			values[Anum_pg_seqam_seqamgetstate - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamgetstate);
+		}
+		else if (pg_strcasecmp(defel->defname, "setstate") == 0)
+		{
+			values[Anum_pg_seqam_seqamsetstate - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamsetstate);
+		}
+		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_seqamgetstate - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method getstate function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamsetstate - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method setstate 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);
+
+	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 6271f8f..e2e1f44 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"
@@ -158,7 +159,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 */
 };
 
 
@@ -1270,6 +1272,10 @@ doDeletion(const ObjectAddress *object, int flags)
 			DropTransformById(object->objectId);
 			break;
 
+		case OCLASS_SEQAM:
+			RemoveSeqAMById(object->objectId);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized object class: %u",
 				 object->classId);
@@ -2381,6 +2387,9 @@ getObjectClass(const ObjectAddress *object)
 
 		case TransformRelationId:
 			return OCLASS_TRANSFORM;
+
+		case SeqAccessMethodRelationId:
+			return OCLASS_SEQAM;
 	}
 
 	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 52d1d59..a420e25 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -437,6 +437,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
 	}
 };
 
@@ -536,7 +548,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 }
 };
 
 const ObjectAddress InvalidObjectAddress =
@@ -691,6 +705,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;
@@ -942,6 +957,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 */
@@ -1002,6 +1020,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 */
@@ -2074,6 +2097,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,
@@ -3024,6 +3048,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,
@@ -3501,6 +3540,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;
@@ -4423,6 +4466,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 80a0eb6..1067ced 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -437,6 +437,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *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 0110b06..46aa2fd 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},
@@ -1088,6 +1089,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:
@@ -1150,6 +1152,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/parser/gram.y b/src/backend/parser/gram.y
index 500c05e..cc9e783 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -592,7 +592,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
@@ -5190,6 +5191,14 @@ DefineStmt:
 					n->definition = list_make1(makeDefElem("from", (Node *) $5));
 					$$ = (Node *)n;
 				}
+			| CREATE ACCESS METHOD FOR SEQUENCES name AS definition
+				{
+					DefineStmt *n = makeNode(DefineStmt);
+					n->kind = OBJECT_SEQAM;
+					n->defnames = list_make1(makeString($6));
+					n->definition = $8;
+					$$ = (Node *)n;
+				}
 		;
 
 definition: '(' def_list ')'						{ $$ = $2; }
@@ -5644,6 +5653,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:
@@ -13535,6 +13545,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 e726831..ff064a8 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"
@@ -1139,6 +1140,11 @@ ProcessUtilitySlow(Node *parsetree,
 							Assert(stmt->args == NIL);
 							DefineCollation(stmt->defnames, stmt->definition);
 							break;
+						case OBJECT_SEQAM:
+							Assert(list_length(stmt->defnames) == 1);
+							Assert(stmt->args == NIL);
+							DefineSeqAM(stmt->defnames, stmt->definition);
+							break;
 						default:
 							elog(ERROR, "unrecognized define stmt type: %d",
 								 (int) stmt->kind);
@@ -2014,6 +2020,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_TRANSFORM:
 					tag = "DROP TRANSFORM";
 					break;
+				case OBJECT_SEQAM:
+					tag = "DROP ACCESS METHOD FOR SEQUENCES";
+					break;
 				default:
 					tag = "???";
 			}
@@ -2110,6 +2119,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/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index d100514..fc76858 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -104,6 +104,7 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
 	int			numForeignServers;
 	int			numDefaultACLs;
 	int			numEventTriggers;
+	int			numSeqAMs;
 
 	if (g_verbose)
 		write_msg(NULL, "reading schemas\n");
@@ -256,6 +257,10 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
 		write_msg(NULL, "reading policies\n");
 	getPolicies(fout, tblinfo, numTables);
 
+	if (g_verbose)
+		write_msg(NULL, "reading sequence access methods\n");
+	getSeqAMs(fout, &numSeqAMs);
+
 	*numTablesPtr = numTables;
 	return tblinfo;
 }
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 573b1de..4a5b3d9 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -179,6 +179,7 @@ static void dumpEventTrigger(Archive *fout, DumpOptions *dopt, EventTriggerInfo
 static void dumpTable(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpAttrDef(Archive *fout, DumpOptions *dopt, AttrDefInfo *adinfo);
+static void dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *tbinfo);
 static void dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo);
 static void dumpIndex(Archive *fout, DumpOptions *dopt, IndxInfo *indxinfo);
@@ -5996,6 +5997,71 @@ getRules(Archive *fout, int *numRules)
 }
 
 /*
+ * getSeqAMs:
+ *	  read all sequence access methods in the system catalogs and return them
+ *	  in the SeqAMInfo* structure
+ *
+ *	numSeqAMs is set to the number of access methods read in
+ */
+SeqAMInfo *
+getSeqAMs(Archive *fout, int *numSeqAMs)
+{
+	PGresult   *res;
+	int			ntups;
+	int			i;
+	PQExpBuffer query;
+	SeqAMInfo  *seqminfo;
+	int			i_tableoid,
+				i_oid,
+				i_seqamname;
+
+	/* Before 9.5, there were no sequence access methods */
+	if (fout->remoteVersion < 90500)
+	{
+		*numSeqAMs = 0;
+		return NULL;
+	}
+
+	query = createPQExpBuffer();
+
+	appendPQExpBuffer(query,
+					  "SELECT tableoid, oid, seqamname "
+					  "FROM pg_catalog.pg_seqam "
+					  "WHERE oid != '%u'::pg_catalog.oid",
+					  LOCAL_SEQAM_OID);
+
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+	ntups = PQntuples(res);
+	*numSeqAMs = ntups;
+
+	seqminfo = (SeqAMInfo *) pg_malloc(ntups * sizeof(SeqAMInfo));
+
+	i_tableoid = PQfnumber(res, "tableoid");
+	i_oid = PQfnumber(res, "oid");
+	i_seqamname = PQfnumber(res, "seqamname");
+
+	for (i = 0; i < ntups; i++)
+	{
+		seqminfo[i].dobj.objType = DO_SEQAM;
+		seqminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+		seqminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+		AssignDumpId(&seqminfo[i].dobj);
+		seqminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_seqamname));
+		seqminfo[i].dobj.namespace = NULL;
+
+		/* Decide whether we want to dump it */
+		selectDumpableObject(&(seqminfo[i].dobj));
+	}
+
+	PQclear(res);
+
+	destroyPQExpBuffer(query);
+
+	return seqminfo;
+}
+
+/*
  * getTriggers
  *	  get information about every trigger on a dumpable table
  *
@@ -8353,6 +8419,9 @@ dumpDumpableObject(Archive *fout, DumpOptions *dopt, DumpableObject *dobj)
 		case DO_POLICY:
 			dumpPolicy(fout, dopt, (PolicyInfo *) dobj);
 			break;
+		case DO_SEQAM:
+			dumpSeqAM(fout, dopt, (SeqAMInfo *) dobj);
+			break;
 		case DO_PRE_DATA_BOUNDARY:
 		case DO_POST_DATA_BOUNDARY:
 			/* never dumped, nothing to do */
@@ -14810,6 +14879,90 @@ findLastBuiltinOid_V70(Archive *fout)
 }
 
 /*
+ * dumpSeqAM
+ *	  write the declaration of one user-defined sequence access method
+ */
+static void
+dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *seqaminfo)
+{
+	PGresult   *res;
+	PQExpBuffer q;
+	PQExpBuffer delq;
+	PQExpBuffer labelq;
+	PQExpBuffer query;
+	char	   *seqamreloptions;
+	char	   *seqaminit;
+	char	   *seqamalloc;
+	char	   *seqamsetval;
+	char	   *seqamgetstate;
+	char	   *seqamsetstate;
+
+	/* Skip if not to be dumped */
+	if (!seqaminfo->dobj.dump || dopt->dataOnly)
+		return;
+
+	q = createPQExpBuffer();
+	delq = createPQExpBuffer();
+	labelq = createPQExpBuffer();
+	query = createPQExpBuffer();
+
+	appendPQExpBuffer(query, "SELECT seqamreloptions, seqaminit, "
+							 "seqamalloc, seqamsetval, "
+							 "seqamgetstate, seqamsetstate "
+							 "FROM pg_catalog.pg_seqam "
+							 "WHERE oid = '%u'::pg_catalog.oid",
+							 seqaminfo->dobj.catId.oid);
+
+	res = ExecuteSqlQueryForSingleRow(fout, query->data);
+
+	seqamreloptions = PQgetvalue(res, 0, PQfnumber(res, "seqamreloptions"));
+	seqaminit = PQgetvalue(res, 0, PQfnumber(res, "seqaminit"));
+	seqamalloc = PQgetvalue(res, 0, PQfnumber(res, "seqamalloc"));
+	seqamsetval = PQgetvalue(res, 0, PQfnumber(res, "seqamsetval"));
+	seqamgetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamgetstate"));
+	seqamsetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamsetstate"));
+
+	appendPQExpBuffer(q, "CREATE ACCESS METHOD FOR SEQUENCES %s AS (\n",
+					  fmtId(seqaminfo->dobj.name));
+
+	appendPQExpBuffer(q, "    RELOPTIONS = %s,\n", seqamreloptions);
+	appendPQExpBuffer(q, "    INIT = %s,\n", seqaminit);
+	appendPQExpBuffer(q, "    ALLOC = %s,\n", seqamalloc);
+	appendPQExpBuffer(q, "    SETVAL = %s,\n", seqamsetval);
+	appendPQExpBuffer(q, "    GETSTATE = %s,\n", seqamgetstate);
+	appendPQExpBuffer(q, "    SETSTATE = %s\n);\n", seqamsetstate);
+
+	appendPQExpBuffer(delq, "DROP ACCESS METHOD FOR SEQUENCES %s",
+					  fmtId(seqaminfo->dobj.name));
+
+	appendPQExpBuffer(labelq, "ACCESS METHOD FOR SEQUENCES %s",
+					  fmtId(seqaminfo->dobj.name));
+
+	if (dopt->binary_upgrade)
+		binary_upgrade_extension_member(q, &seqaminfo->dobj, labelq->data);
+
+	ArchiveEntry(fout, seqaminfo->dobj.catId, seqaminfo->dobj.dumpId,
+				 seqaminfo->dobj.name,
+				 NULL,
+				 NULL,
+				 "",
+				 false, "ACCESS METHOD FOR SEQUENCES", SECTION_PRE_DATA,
+				 q->data, delq->data, NULL,
+				 NULL, 0,
+				 NULL, NULL);
+
+	/* Dump Parser Comments */
+	dumpComment(fout, dopt, labelq->data,
+				NULL, "",
+				seqaminfo->dobj.catId, 0, seqaminfo->dobj.dumpId);
+
+	PQclear(res);
+	destroyPQExpBuffer(q);
+	destroyPQExpBuffer(delq);
+	destroyPQExpBuffer(labelq);
+}
+
+/*
  * dumpSequence
  *	  write the declaration (not data) of one user-defined sequence
  */
@@ -14911,8 +15064,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	{
 		PGresult   *res2;
 
-		printfPQExpBuffer(query, "SELECT a.seqamname\n"
-								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+		printfPQExpBuffer(query, "SELECT a.seqamname "
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c "
 								 "WHERE c.relam = a.oid AND c.oid = %u",
 						  tbinfo->dobj.catId.oid);
 
@@ -16017,6 +16170,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
 			case DO_FOREIGN_SERVER:
 			case DO_TRANSFORM:
 			case DO_BLOB:
+			case DO_SEQAM:
 				/* Pre-data objects: must come before the pre-data boundary */
 				addObjectDependency(preDataBound, dobj->dumpId);
 				break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 3d2b66a..141c684 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -77,7 +77,8 @@ typedef enum
 	DO_POST_DATA_BOUNDARY,
 	DO_EVENT_TRIGGER,
 	DO_REFRESH_MATVIEW,
-	DO_POLICY
+	DO_POLICY,
+	DO_SEQAM
 } DumpableObjectType;
 
 typedef struct _dumpableObject
@@ -305,6 +306,11 @@ typedef struct _ruleInfo
 	/* reloptions is only set if we need to dump the options with the rule */
 } RuleInfo;
 
+typedef struct _seqamInfo
+{
+	DumpableObject dobj;
+} SeqAMInfo;
+
 typedef struct _triggerInfo
 {
 	DumpableObject dobj;
@@ -561,5 +567,6 @@ extern void getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionIn
 					   int numExtensions);
 extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
 extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);
+extern SeqAMInfo *getSeqAMs(Archive *fout, int *numSeqAMs);
 
 #endif   /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index aa3e20a..ad81f0d 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -74,7 +74,8 @@ static const int oldObjectTypePriority[] =
 	13,							/* DO_POST_DATA_BOUNDARY */
 	20,							/* DO_EVENT_TRIGGER */
 	15,							/* DO_REFRESH_MATVIEW */
-	21							/* DO_POLICY */
+	21,							/* DO_POLICY */
+	5							/* DO_SEQAM */
 };
 
 /*
@@ -124,7 +125,8 @@ static const int newObjectTypePriority[] =
 	25,							/* DO_POST_DATA_BOUNDARY */
 	32,							/* DO_EVENT_TRIGGER */
 	33,							/* DO_REFRESH_MATVIEW */
-	34							/* DO_POLICY */
+	34,							/* DO_POLICY */
+	17							/* DO_SEQAM */
 };
 
 static DumpId preDataBoundId;
@@ -1469,6 +1471,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
 					 "POLICY (ID %d OID %u)",
 					 obj->dumpId, obj->catId.oid);
 			return;
+		case DO_SEQAM:
+			snprintf(buf, bufsize,
+					 "ACCESS METHOD FOR SEQUENCES %s  (ID %d OID %u)",
+					 obj->name, obj->dumpId, obj->catId.oid);
+			return;
 		case DO_PRE_DATA_BOUNDARY:
 			snprintf(buf, bufsize,
 					 "PRE-DATA BOUNDARY  (ID %d)",
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
index 5040118..913cbd7 100644
--- a/src/include/access/seqam.h
+++ b/src/include/access/seqam.h
@@ -28,6 +28,9 @@ typedef struct SequenceHandle SequenceHandle;
 
 extern char *serial_seqam;
 
+extern Oid DefineSeqAM(List *names, List *definition);
+extern void RemoveSeqAMById(Oid seqamoid);
+
 extern HeapTuple seqam_init(Relation seqrel, HeapTuple tuple,
 							int64 restart_value, bool restart_requested,
 							bool is_init);
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 5da18c2..42a2fa4 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -149,6 +149,7 @@ typedef enum ObjectClass
 	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
 	OCLASS_POLICY,				/* pg_policy */
 	OCLASS_TRANSFORM,			/* pg_transform */
+	OCLASS_SEQAM,				/* pg_seqam */
 	MAX_OCLASS					/* MUST BE LAST */
 } ObjectClass;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index c55c9de..71a208f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1261,6 +1261,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 5b1ee15..69f547c 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

0003-gapless-sequence-v5.patchapplication/x-patch; name=0003-gapless-sequence-v5.patchDownload
From afbc19973d6db1d5c6ad518c9d376d26b4c27517 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Tue, 9 Dec 2014 22:57:53 +0100
Subject: [PATCH 3/3] gapless sequence v5

---
 contrib/gapless_seq/Makefile                 |  63 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  78 ++++
 contrib/gapless_seq/gapless_seq.c            | 536 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/gapless_seq.sgml         |  22 ++
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 +++
 9 files changed, 971 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/gapless_seq.sgml
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql

diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..c2dd1be
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+   pg_sequence_get_state    
+----------------------------
+ {last_value,2,is_called,t}
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+   pg_sequence_get_state    
+----------------------------
+ {last_value,2,is_called,t}
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+ERROR:  cannot drop sequence access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on sequence access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..61f9c22
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,78 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_reloptions(INTERNAL, BOOLEAN)
+RETURNS BYTEA
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_init(INTERNAL, INTERNAL, BIGINT, BOOLEAN, BOOLEAN)
+RETURNS BYTEA
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_alloc(INTERNAL, INTERNAL, BIGINT, INTERNAL)
+RETURNS BIGINT
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_setval(INTERNAL, INTERNAL, BIGINT)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_get_state(INTERNAL, INTERNAL)
+RETURNS TEXT[]
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_set_state(INTERNAL, INTERNAL, TEXT[])
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	is_called bool NOT NULL,
+	last_value bigint NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relam = (
+			SELECT oid FROM pg_seqam WHERE seqamname = 'gapless'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD FOR SEQUENCES gapless AS (
+	reloptions = seqam_gapless_reloptions,	/* reloptions parser is same as local (no reloptions) */
+	init = seqam_gapless_init,				/* init new gapless sequence */
+	alloc = seqam_gapless_alloc,			/* logs and returns each value... slow */
+	setval = seqam_gapless_setval,			/* setval support */
+	getstate = seqam_gapless_get_state,		/* pgdump support */
+	setstate = seqam_gapless_set_state		/* pgdump support */
+);
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..c7c21d5
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,536 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = Gapless functions
+ *
+ *------------------------------------------------------------
+ */
+extern Datum seqam_gapless_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_init(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_set_state(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_reloptions);
+PG_FUNCTION_INFO_V1(seqam_gapless_init);
+PG_FUNCTION_INFO_V1(seqam_gapless_alloc);
+PG_FUNCTION_INFO_V1(seqam_gapless_setval);
+PG_FUNCTION_INFO_V1(seqam_gapless_get_state);
+PG_FUNCTION_INFO_V1(seqam_gapless_set_state);
+
+typedef struct GaplessAmdata
+{
+	uint32		xid;
+} GaplessAmdata;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	bool	is_called;
+	int64	last_value;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, int64 last_value,
+							   bool is_called, HeapTuple oldtuple);
+
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+Datum
+seqam_gapless_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum		reloptions = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+Datum
+seqam_gapless_init(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	HeapTuple	tuple = (HeapTuple) PG_GETARG_POINTER(1);
+	int64		restart_value = (int64) PG_GETARG_INT64(2);
+	bool		restart_requested = (bool) PG_GETARG_BOOL(3);
+	bool		is_init = (bool) PG_GETARG_BOOL(4);
+	Oid			seqrelid = seqrel->rd_id;
+	TupleDesc	tupDesc = RelationGetDescr(seqrel);
+	Datum		amdata;
+	bool		isnull;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	TransactionId		local_xid = GetTopTransactionId();
+	GaplessAmdata	   *gapless_seq;
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, restart_value, false, valtuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+						 &isnull);
+
+	/* Make sure amdata is filled. */
+	if (isnull)
+	{
+		Datum			values[SEQ_COL_LASTCOL];
+		bool			nulls[SEQ_COL_LASTCOL];
+		bool			replace[SEQ_COL_LASTCOL];
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(GaplessAmdata));
+		HeapTuple		newtup;
+
+		memset(replace, false, sizeof(replace));
+		memset(nulls, false, sizeof(nulls));
+		memset(values, 0, sizeof(values));
+
+		replace[SEQ_COL_AMDATA - 1] = true;
+		nulls[SEQ_COL_AMDATA - 1] = false;
+
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(GaplessAmdata));
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replace);
+
+		/* Don't leak memory. */
+		heap_freetuple(tuple);
+		tuple = newtup;
+
+		amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+							 &isnull);
+	}
+
+	gapless_seq = (GaplessAmdata *)
+		VARDATA_ANY(DatumGetByteaP(amdata));
+
+	/* Update the xid info */
+	gapless_seq->xid = UInt32GetDatum(local_xid);
+
+	PG_RETURN_POINTER(tuple);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+Datum
+seqam_gapless_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	/* we ignore nreguested as gapless sequence can't do caching */
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	FormData_pg_sequence   *seq;
+	GaplessAmdata	   *gapless_seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	gapless_seq = (GaplessAmdata *) VARDATA_ANY(&seq->amdata);
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/*
+	 * Insert or update the last value tuple.
+	 */
+	set_last_value_tup(valrel, seqrelid, result, true, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/*
+	 * If current tx is different fron the last one,
+	 * update the sequence tuple as well.
+	 *
+	 * We don't need to WAL log the update as the only thing we save to
+	 * sequence tuple is the active transaction id and we know that in case of
+	 * crash the transaction id will not be active so it's ok to lose the
+	 * update.
+	 */
+	if (gapless_seq->xid != local_xid)
+	{
+		sequence_start_update(seqh, true);
+		gapless_seq->xid = local_xid;
+		sequence_apply_update(seqh, true);
+		sequence_finish_update(seqh);
+	}
+
+	*last = result;
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+Datum
+seqam_gapless_setval(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+Datum
+seqam_gapless_get_state(PG_FUNCTION_ARGS)
+{
+	Relation		seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	Datum			datums[4];
+	Datum			val;
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	int64			last_value;
+	bool			is_called;
+
+	/*
+	 * Get the last value from the values table, if not found use start_value
+	 * from the sequence definition.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		last_value = v->last_value;
+		is_called = v->is_called;
+	}
+	else
+	{
+		FormData_pg_sequence *seq = (FormData_pg_sequence *)
+			GETSTRUCT(sequence_read_tuple(seqh));
+		last_value = seq->start_value;
+		is_called = false;
+		sequence_release_tuple(seqh);
+	}
+
+	datums[0] = CStringGetTextDatum("last_value");
+	val = DirectFunctionCall1(int8out, Int64GetDatum(last_value));
+	datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+	datums[2] = CStringGetTextDatum("is_called");
+	val = DirectFunctionCall1(boolout, BoolGetDatum(is_called));
+	datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+	PG_RETURN_ARRAYTYPE_P(construct_array(datums, 4, TEXTOID, -1, false, 'i'));
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+Datum
+seqam_gapless_set_state(PG_FUNCTION_ARGS)
+{
+	Relation		seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	ArrayType	   *array = PG_GETARG_ARRAYTYPE_P(2);
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	int64			last_value = 0;
+	bool			is_called = false;
+	bool			last_value_found = false,
+					is_called_found = false;
+	Datum		   *datums;
+	int				count;
+	int				i;
+	FormData_pg_sequence   *seq;
+	GaplessAmdata	   *gapless_seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	Assert(ARR_ELEMTYPE(array) == TEXTOID && ARR_NDIM(array) == 1 &&
+		   (ARR_DIMS(array)[0]) % 2 == 0 && !ARR_HASNULL(array));
+
+	deconstruct_array(array,
+					  TEXTOID, -1, false, 'i',
+					  &datums, NULL, &count);
+
+	count /= 2;
+	for (i = 0; i < count; ++i)
+	{
+		char   *key,
+			   *val;
+
+		key = TextDatumGetCString(datums[i * 2]);
+		val = TextDatumGetCString(datums[i * 2 + 1]);
+
+		if (pg_strcasecmp(key, "last_value") == 0)
+		{
+			last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+														CStringGetDatum(val)));
+			last_value_found = true;
+		}
+		else if (pg_strcasecmp(key, "is_called") == 0)
+		{
+			is_called = DatumGetBool(DirectFunctionCall1(boolin,
+														CStringGetDatum(val)));
+			is_called_found = true;
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for gapless sequence",
+							key)));
+	}
+
+	if (!last_value_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("last_value is required parameter for gapless sequence")));
+
+	if (!is_called_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("is_called is required parameter for gapless sequence")));
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	gapless_seq = (GaplessAmdata *) VARDATA_ANY(&seq->amdata);
+
+	sequence_check_range(last_value, seq->min_value, seq->max_value, "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, last_value, is_called, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	gapless_seq->xid = local_xid;
+	sequence_apply_update(seqh, true);
+	sequence_finish_update(seqh);
+	sequence_release_tuple(seqh);
+
+	PG_FREE_IF_COPY(array, 2);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormData_pg_sequence   *seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	GaplessAmdata	   *gapless_seq = (GaplessAmdata *) VARDATA_ANY(&seq->amdata);
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (gapless_seq->xid != local_xid &&
+		   TransactionIdIsInProgress(gapless_seq->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(gapless_seq->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, int64 last_value, bool is_called, HeapTuple oldtuple)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/gapless_seq.sgml b/contrib/gapless_seq/gapless_seq.sgml
new file mode 100644
index 0000000..3eb536f
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.sgml
@@ -0,0 +1,22 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless_seq" xreflabel="gapless_seq">
+ <title>gapless sequence</title>
+
+ <indexterm zone="gapless_seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence.
+  This could be very costly and is not recommended for general
+  usage except in specific applications that require this feature.
+ </para>
+
+</sect1>
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..1913947
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
-- 
1.9.1

#76Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Petr Jelinek (#75)
1 attachment(s)
Re: Sequence Access Method WIP

Heikki, do you have time to go through this at this point?

Petr Jelinek wrote:

I merged all your changes and merged the patch#1 with patch#4.

Also I pushed my repo to https://github.com/PJMODOS/postgres/tree/seqam and
gave you (Heikki) commit rights there in case you want to change anything.

I rebased your #1 to current master; attached. It builds and passes
regression test, but I didn't check any further. Doc build fails with:

$ make check
onsgmls -wall -wno-unused-param -wno-empty -wfully-tagged -D . -D /pgsql/source/master/doc/src/sgml -s /pgsql/source/master/doc/src/sgml/postgres.sgml
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:185:3:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:185:44:E: document type does not allow element "LITERAL" here; missing one of "REMARK", "SYNOPSIS", "LITERALLAYOUT", "PROGRAMLISTING", "SCREEN", "PARA", "SIMPARA", "BRIDGEHEAD" start-tag
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:185:54:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:186:53:E: element "STRUCT" undefined
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:186:71:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:187:31:E: document type does not allow element "FUNCTION" here; missing one of "REMARK", "SYNOPSIS", "LITERALLAYOUT", "PROGRAMLISTING", "SCREEN", "PARA", "SIMPARA", "BRIDGEHEAD" start-tag
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:187:55:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:189:12:E: document type does not allow element "FUNCTION" here; missing one of "REMARK", "SYNOPSIS", "LITERALLAYOUT", "PROGRAMLISTING", "SCREEN", "PARA", "SIMPARA", "BRIDGEHEAD" start-tag
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:189:38:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:190:28:E: document type does not allow element "FUNCTION" here; missing one of "REMARK", "SYNOPSIS", "LITERALLAYOUT", "PROGRAMLISTING", "SCREEN", "PARA", "SIMPARA", "BRIDGEHEAD" start-tag
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:190:54:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:191:8:E: end tag for element "PARA" which is not open
Makefile:320: recipe for target 'check' failed
make: *** [check] Error 1

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

seqam-v9ah.patchtext/x-diff; charset=us-asciiDownload
commit b75a5724f210b34a2864e7789735899a4ce22be0
Author: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date:   Tue May 12 23:14:10 2015 -0300

    seqam

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 2756652..8faa87b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -259,6 +259,11 @@
      </row>
 
      <row>
+      <entry><link linkend="catalog-pg-seqam"><structname>pg_seqam</structname></link></entry>
+      <entry>sequence access methods</entry>
+     </row>
+
+     <row>
       <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
       <entry>dependencies on shared objects</entry>
      </row>
@@ -5659,6 +5664,95 @@
   </table>
  </sect1>
 
+ <sect1 id="catalog-pg-seqam">
+  <title><structname>pg_seqam</structname></title>
+
+  <indexterm zone="catalog-pg-seqam">
+   <primary>pg_seqam</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_seqam</structname> stores information about
+   sequence access methods. There is one row for each sequence access method
+   installed on the system.
+  </para>
+
+  <table>
+   <title><structname>pg_seqam</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+
+     <row>
+      <entry><structfield>oid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry></entry>
+      <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamreloptions</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to parse and validate <structfield>reloptions</> for the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqaminit</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function called during initialization or <command>RESET</command> of a sequence</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamalloc</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry><quote>Allocate new sequence id</quote> function</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetval</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function implementing <function>setval()</function> interface</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamgetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to dump current state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to restore a dumped state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="catalog-pg-shdepend">
   <title><structname>pg_shdepend</structname></title>
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 0d8624a..dd12625 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5671,6 +5671,27 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-serial-sequenceam" xreflabel="serial_sequenceam">
+      <term><varname>serial_sequenceam</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>serial_sequenceam</> configuration parameter</primary>
+      </indexterm>
+      <indexterm><primary>sequence access method</><secondary>serial</></>
+      </term>
+      <listitem>
+       <para>
+        This variable specifies the default sequence access method to be used
+        for <type>SERIAL</> and <type>BIGSERIAL</>.
+       </para>
+
+       <para>
+        The default is 'local' sequence access method. If the value does not
+        match the name of any existing sequence access method, an error will be
+        raised.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-check-function-bodies" xreflabel="check_function_bodies">
       <term><varname>check_function_bodies</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 6268d54..72daef7 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 4a45138..7dfe877 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -250,6 +250,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..a0aaa4b
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,289 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_seqam</structname> system catalog (see
+   <xref linkend="catalog-pg-seqam">).  The contents of a
+   <structname>pg_seqam</structname> row is the name of the access method
+   and are references to
+   <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>
+   entries that identify the functions provided by the access method.
+   functions supplied by the access method.
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The behaviour of a sequence access method is defined by a set of functions.
+   These functions are implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+HeapTuple
+seqam_init (Relation seqrel, HeapTuple tuple, int64 restart_value,
+            bool restart_requested, bool is_init);
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   <literal>tuple</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the updated
+   <structname>pg_sequence</structname>. If the tuple does not have to be
+   updated returning the input <literal>tuple</> is acceptable.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+seqam_reloptions (ArrayType *reloptions, bool validate);
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value, which will be then sent
+   to the <function>seqam_init</> and stored in the catalog.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+seqam_alloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+             int64 *last);
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+bool
+seqam_setval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+ArrayType *
+seqam_get_state (Relation seqrel, SequenceHandle *seqh);
+</programlisting>
+   Dump the current state of the sequence. The return value is one dimensional
+   <literal>TEXT</> array containing list of key/value pairs it the form
+   <literal>{'key1', 'value1', 'key2', 'value2'}</>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void
+seqam_set_state (Relation seqrel, SequenceHandle *seqh, ArrayType *statearr);
+</programlisting>
+   Restore state of the sequence based on the key/value pairs defined in
+   the <literal>statearr</> in <literal>{'key1', 'value1', 'key2', 'value2'}</>
+   form. The <literal>statearr</> is defined as <literal>TEXT[]</> in SQL. This
+   function must accept output of the <function>seqam_get_state()</> function.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid relid, SequenceHandle *seqh);
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh);
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+HeapTuple
+sequence_read_tuple (SequenceHandle *seqh);
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+<programlisting>
+HeapTuple
+sequence_swap_tuple (SequenceHandle *seqh, HeapTuple *newtup);
+</programlisting>
+   Changes the working tuple to the <literal>newtup</>. This does not change
+   the sequence itself, only the state of the <struct>SequenceHandle</>. To
+   save the tuple the <function>sequence_*_update()</> sequence has to be
+   called. The tuple should be released by calling
+   <function>sequence_release_tuple</>. Note that you can't call this function
+   without calling <function>sequence_read_tuple()</> first.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal);
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_apply_update(SequenceHandle *seqh, bool do_wal);
+</programlisting>
+   Save the modified sequence tuple indicating if the change should be WAL
+   logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh, HeapTuple newtuple);
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_apply_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh);
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_tuple</> and/or
+   added by <function>sequence_swap_tuple</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh);
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_apply_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors);
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname);
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..9fcb9b2 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree \
+	      rmgrdesc sequence spgist transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 8176b6a..9543503 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -829,7 +829,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -860,7 +861,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+			options = am_reloptions(amoptions, datum, false);
+			break;
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1308,14 +1312,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..161ec21
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,308 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amdata.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAM data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char	*serial_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_seqaminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Method API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_get_state		- dump sequence state (for pg_dump)
+ *		seqam_set_state		- restore sequence state (for pg_dump)
+ *
+ * Additionally, the am_reloptions interface is used for parsing reloptions
+ * that can be used for passing AM specific options.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+HeapTuple
+seqam_init(Relation seqrel, HeapTuple tuple, int64 restart_value,
+		   bool restart_requested, bool is_init)
+{
+	FmgrInfo	procedure;
+	Datum		ret;
+
+	fmgr_info(seqrel->rd_seqam->seqaminit, &procedure);
+
+	/*
+	 * Have the seqam's proc do its work.
+	 */
+	ret = FunctionCall5(&procedure,
+						PointerGetDatum(seqrel),
+						PointerGetDatum(tuple),
+						Int64GetDatum(restart_value),
+						BoolGetDatum(restart_requested),
+						BoolGetDatum(is_init));
+
+	return (HeapTuple) DatumGetPointer(ret);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do its work.
+	 */
+	ret = FunctionCall4(procedure,
+						PointerGetDatum(seqrel),
+						PointerGetDatum(seqh),
+						Int64GetDatum(nrequested),
+						PointerGetDatum(last));
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	FunctionCall3(procedure,
+				  PointerGetDatum(seqrel),
+				  PointerGetDatum(seqh),
+				  Int64GetDatum(new_value));
+}
+
+/*
+ * seqam_get_state - pg_dump support
+ */
+ArrayType *
+seqam_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	FmgrInfo	procedure;
+	Datum		statearr;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamgetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	statearr = FunctionCall2(&procedure,
+							 PointerGetDatum(seqrel),
+							 PointerGetDatum(seqh));
+
+	return (ArrayType *) DatumGetPointer(statearr);
+}
+
+/*
+ * seqam_set_state - restore from pg_dump
+ */
+void
+seqam_set_state(Relation seqrel, SequenceHandle *seqh, ArrayType *statearr)
+{
+	FmgrInfo	procedure;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamsetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	FunctionCall3(&procedure,
+				  PointerGetDatum(seqrel),
+				  PointerGetDatum(seqh),
+				  PointerGetDatum(statearr));
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new serial_seqam value */
+bool
+check_serial_seqam(char **newval, void **extra, GucSource source)
+{
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the name.  Must accept the value on faith.
+	 */
+	if (IsTransactionState())
+	{
+		if (!OidIsValid(get_seqam_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, we are checking the argument of an
+			 * ALTER DATABASE SET or ALTER USER SET command.  Value may
+			 * be created later.  Because of that, issue a NOTICE if source ==
+			 * PGC_S_TEST, but accept the value anyway.
+			 */
+			if (source == PGC_S_TEST)
+			{
+				ereport(NOTICE,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("sequence access method \"%s\" does not exist",
+								*newval)));
+			}
+			else
+			{
+				GUC_check_errdetail("sequence access method \"%s\" does not exist.",
+									*newval);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..a891bd8
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,383 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct LocalAmdata
+{
+	int64           log_cnt;
+} LocalAmdata;
+
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum		reloptions = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	HeapTuple	tuple = (HeapTuple) PG_GETARG_POINTER(1);
+	int64		restart_value = PG_GETARG_INT64(2);
+	bool		restart_requested = PG_GETARG_BOOL(3);
+	bool		is_init = PG_GETARG_BOOL(4);
+	bool		isnull;
+	Datum		amdata;
+	TupleDesc	tupDesc = RelationGetDescr(seqrel);
+	Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(tuple);
+	LocalAmdata *localseq;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seq->last_value = restart_value;
+		seq->is_called = false;
+	}
+
+	amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+						 &isnull);
+
+	/* Make sure amdata is filled. */
+	if (isnull)
+	{
+		Datum			values[SEQ_COL_LASTCOL];
+		bool			nulls[SEQ_COL_LASTCOL];
+		bool			replace[SEQ_COL_LASTCOL];
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(LocalAmdata));
+		HeapTuple		newtup;
+
+		memset(replace, false, sizeof(replace));
+		memset(nulls, false, sizeof(nulls));
+		memset(values, 0, sizeof(values));
+
+		replace[SEQ_COL_AMDATA - 1] = true;
+		nulls[SEQ_COL_AMDATA - 1] = false;
+
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(LocalAmdata));
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replace);
+
+		/* Don't leak memory. */
+		heap_freetuple(tuple);
+		tuple = newtup;
+
+		amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+							 &isnull);
+	}
+
+	localseq = (LocalAmdata *)
+		VARDATA_ANY(DatumGetByteaP(amdata));
+
+	/* We always reset the log_cnt. */
+	localseq->log_cnt = 0;
+
+	PG_RETURN_POINTER(tuple);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormData_pg_sequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	LocalAmdata *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+	next = result = seq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = seq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	sequence_start_update(seqh, logit);
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		localseq->log_cnt = 0;
+
+		sequence_apply_update(seqh, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = log;		/* how much is logged */
+
+	sequence_apply_update(seqh, false);
+	sequence_finish_update(seqh);
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	int64		next = PG_GETARG_INT64(2);
+	FormData_pg_sequence *seq;
+	LocalAmdata *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = 0;		/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_apply_update(seqh, true);
+	sequence_finish_update(seqh);
+	sequence_release_tuple(seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+Datum
+seqam_local_get_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	Datum			datums[4];
+	Datum			val;
+	FormData_pg_sequence *seq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	datums[0] = CStringGetTextDatum("last_value");
+	val = DirectFunctionCall1(int8out, Int64GetDatum(seq->last_value));
+	datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+	datums[2] = CStringGetTextDatum("is_called");
+	val = DirectFunctionCall1(boolout, BoolGetDatum(seq->is_called));
+	datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+	sequence_release_tuple(seqh);
+
+	PG_RETURN_ARRAYTYPE_P(construct_array(datums, 4, TEXTOID, -1, false, 'i'));
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+Datum
+seqam_local_set_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	ArrayType	   *statearr = PG_GETARG_ARRAYTYPE_P(2);
+	FormData_pg_sequence *seq;
+	int64			last_value = 0;
+	bool			is_called = false;
+	bool			last_value_found = false,
+					is_called_found = false;
+	Datum		   *datums;
+	int				count;
+	int				i;
+
+	Assert(ARR_ELEMTYPE(statearr) == TEXTOID && ARR_NDIM(statearr) == 1 &&
+		   (ARR_DIMS(statearr)[0]) % 2 == 0 && !ARR_HASNULL(statearr));
+
+	deconstruct_array(statearr,
+					  TEXTOID, -1, false, 'i',
+					  &datums, NULL, &count);
+	count /= 2;
+	for (i = 0; i < count; ++i)
+	{
+		char   *key,
+			   *val;
+
+		key = TextDatumGetCString(datums[i * 2]);
+		val = TextDatumGetCString(datums[i * 2 + 1]);
+
+		if (pg_strcasecmp(key, "last_value") == 0)
+		{
+			last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+														CStringGetDatum(val)));
+			last_value_found = true;
+		}
+		else if (pg_strcasecmp(key, "is_called") == 0)
+		{
+			is_called = DatumGetBool(DirectFunctionCall1(boolin,
+														CStringGetDatum(val)));
+			is_called_found = true;
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for local sequence",
+							key)));
+	}
+
+	if (!last_value_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("last_value is required parameter for local sequence")));
+
+	if (!is_called_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("is_called is required parameter for local sequence")));
+
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	sequence_check_range(last_value, seq->min_value, seq->max_value, "last_value");
+
+	sequence_start_update(seqh, true);
+	seq->last_value = last_value;
+	seq->is_called = is_called;
+	sequence_apply_update(seqh, true);
+	sequence_finish_update(seqh);
+	sequence_release_tuple(seqh);
+
+	PG_FREE_IF_COPY(statearr, 2);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 6e563b6..1c85569 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -252,6 +252,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 37d05d1..d1043ba 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d04e94d..1fd46a6 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -87,6 +87,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -847,6 +848,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -920,6 +922,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1033,6 +1036,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1261,6 +1265,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1273,7 +1278,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1328,6 +1334,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = SeqAccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index a1f8ada..cbb0f5f 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index c99d353..0b35046 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -290,6 +290,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 3febdd5..e2c1990 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -678,6 +678,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e8f0d79..f7bfca0 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 351d48e..bcf1065 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -527,7 +527,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 80f5553..d2f52bb 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,6 +14,9 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
@@ -22,8 +25,10 @@
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +41,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,6 +81,16 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTupleData tup;
+	Page		temppage;
+	bool		inupdate;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -91,13 +101,11 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
 
 
 /*
@@ -111,6 +119,7 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
@@ -147,6 +156,11 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
+	if (seq->accessMethod)
+		seqamid = get_seqam_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
 	/*
 	 * Create relation (and fill value[] and null[] for the tuple)
 	 */
@@ -207,11 +221,6 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "cache_value";
 				value[i - 1] = Int64GetDatumFast(new.cache_value);
 				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
 			case SEQ_COL_CYCLE:
 				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
 				coldef->colname = "is_cycled";
@@ -222,6 +231,12 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "is_called";
 				value[i - 1] = BoolGetDatum(false);
 				break;
+			case SEQ_COL_AMDATA:
+				coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+				coldef->colname = "amdata";
+				null[i - 1] = true;
+				value[i - 1] = (Datum) 0;
+				break;
 		}
 		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
@@ -229,12 +244,13 @@ DefineSequence(CreateSeqStmt *seq)
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
@@ -243,6 +259,7 @@ DefineSequence(CreateSeqStmt *seq)
 
 	/* now initialize the sequence's data */
 	tuple = heap_form_tuple(tupDesc, value, null);
+	tuple = seqam_init(rel, tuple, new.start_value, false, true);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +271,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +285,55 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tuple = seqam_init(seqrel, tuple, seq->start_value, true, false);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
 }
 
 /*
@@ -361,7 +376,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +396,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -406,19 +411,23 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
 	ObjectAddress address;
+	List	   *seqoptions;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +435,173 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	seqoptions = stmt->options;
+	init_params(seqoptions, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	oldamid = seqrel->rd_rel->relam;
+	if (stmt->accessMethod)
+		seqamid = get_seqam_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(seqoptions, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		Form_pg_seqam	seqam;
+		HeapTuple       seqamtup;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = seqam_alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		seqam = (Form_pg_seqam) GETSTRUCT(seqamtup);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		validnsps[0] = NameStr(seqam->seqamname);
+		validnsps[1] = NULL;
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		PageSetLSN(page, recptr);
-	}
+		(void) am_reloptions(seqam->seqamreloptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-	END_CRIT_SECTION();
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-	UnlockReleaseBuffer(buf);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
+
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
+
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/* Let the new sequence AM initialize. */
+		tuple = seqam_init(seqrel, tuple, restart_value, true, true);
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * See ResetSequence for why we do this.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		/*
+		 * Insert the modified tuple into the new storage file.
+		 */
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										SeqAccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = SeqAccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+	else
+	{
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
+
+		/* Let sequence AM update the tuple. */
+		tuple = seqam_init(seqrel, tuple, restart_value, restart_requested, false);
+		sequence_swap_tuple(&seqh, tuple);
+		sequence_start_update(&seqh, true);
+		sequence_apply_update(&seqh, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +642,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +684,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +701,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for
-	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
-	 * no need to assign xids subxacts, that'll already trigger a appropriate
-	 * wait.  (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +712,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +773,24 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,92 +807,27 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam_setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
-}
-
-/*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
+	last_used_seq = elm;
 
-	do_setval(relid, next, true);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
 /*
  * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
 setval3_oid(PG_FUNCTION_ARGS)
@@ -969,12 +835,66 @@ setval3_oid(PG_FUNCTION_ARGS)
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
 	bool		iscalled = PG_GETARG_BOOL(2);
+	ArrayType  *statearr;
+	Datum		datums[4];
+	Datum		val;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
 
-	do_setval(relid, next, iscalled);
-
-	PG_RETURN_INT64(next);
-}
-
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
+
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Convert the data into 'local' sequence dump format and call restore API. */
+	datums[0] = CStringGetTextDatum("last_value");
+	val = DirectFunctionCall1(int8out, Int64GetDatum(next));
+	datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+	datums[2] = CStringGetTextDatum("is_called");
+	val = DirectFunctionCall1(boolout, BoolGetDatum(iscalled));
+	datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+	statearr = construct_array(datums, 4, TEXTOID, -1, false, 'i');
+
+	seqam_set_state(seqh.rel, &seqh, statearr);
+
+	pfree(statearr);
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
+
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
+
+	sequence_close(&seqh);
+
+	PG_RETURN_INT64(next);
+}
 
 /*
  * Open the sequence and acquire AccessShareLock if needed
@@ -1034,11 +954,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1009,56 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->temppage = NULL;
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(seqh->temppage == NULL && !seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
 
-	page = BufferGetPage(*buf);
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1068,185 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
 	}
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+/*
+ * Swap the current working tuple.
+ *
+ * Note that the changes are only saved to in-memory state and will not be
+ * visible unless the sequence_*_update sequence is called.
+ */
+void
+sequence_swap_tuple(SequenceHandle *seqh, HeapTuple newtup)
+{
+	Page	page;
+
+	Assert(!seqh->inupdate && seqh->tup.t_data != NULL &&
+		   seqh->temppage == NULL);
+
+	page = BufferGetPage(seqh->buf);
+
+	seqh->temppage = PageGetTempPageCopySpecial(page);
+
+	/* Sequence tuples are always frozen. */
+	HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+	HeapTupleHeaderSetXminFrozen(newtup->t_data);
+	HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+	HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+	newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+	ItemPointerSet(&newtup->t_data->t_ctid, 0, FirstOffsetNumber);
+
+	if (PageAddItem(seqh->temppage, (Item) newtup->t_data, newtup->t_len,
+				FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+		elog(PANIC, "sequence_apply_update: failed to add item to page");
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	PageSetLSN(seqh->temppage, PageGetLSN(page));
 
-	return seq;
+	seqh->tup.t_data = newtup->t_data;
+	seqh->tup.t_len = newtup->t_len;
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+	if (do_wal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_apply_update(SequenceHandle *seqh, bool do_wal)
+{
+	Page	page;
+
+	Assert(seqh->inupdate && seqh->tup.t_data != NULL);
+
+	page = BufferGetPage(seqh->buf);
+
+	/*
+	 * If the working tuple was swapped we need to copy it to the page
+	 */
+	if (seqh->temppage != NULL)
+	{
+		PageRestoreTempPage(seqh->temppage, page);
+		seqh->temppage = NULL;
+	}
+
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (do_wal)
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate && seqh->temppage == NULL);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	if (seqh->temppage)
+	{
+		pfree(seqh->temppage);
+		seqh->temppage = NULL;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
+	{
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
+	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
+
+	Assert(BufferIsValid(seqh->buf));
+
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
+
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
+}
+
+/*
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1256,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1333,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1341,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1350,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1358,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1365,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1378,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1431,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1445,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1555,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1583,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1591,89 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *statearr;
+	SequenceHandle seqh;
+
+	sequence_open(relid, &seqh);
+
+	statearr = seqam_get_state(seqh.rel, &seqh);
+
+	sequence_close(&seqh);
+
+	Assert(ARR_ELEMTYPE(statearr) == TEXTOID && ARR_NDIM(statearr) == 1 &&
+		   (ARR_DIMS(statearr)[0]) % 2 == 0 && !ARR_HASNULL(statearr));
+
+	PG_RETURN_ARRAYTYPE_P(statearr);
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *statearr = PG_GETARG_ARRAYTYPE_P(1);
+	SequenceHandle seqh;
+
+	Assert(ARR_ELEMTYPE(statearr) == TEXTOID);
+
+	/*
+	 * Do the input checks.
+	 */
+	if (ARR_NDIM(statearr) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state must be one dimensional array")));
+
+	if ((ARR_DIMS(statearr)[0]) % 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state array must have even number of elements")));
+
+	if (array_contains_nulls(statearr))
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state array cannot contain NULLs")));
+
+	/* Call in the sequence. */
+	sequence_open(relid, &seqh);
+
+	seqam_set_state(seqh.rel, &seqh, statearr);
+
+	sequence_close(&seqh);
+
+	PG_FREE_IF_COPY(statearr, 1);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1739,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0a6b069..86c0c5f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -266,6 +266,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -446,7 +447,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -465,7 +466,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -543,13 +543,43 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		HeapTuple	tuple;
+		Form_pg_seqam seqam;
+		static char *validnsps[2];
+
+		Assert(relamid != InvalidOid);
+
+		tuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(relamid));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 relamid);
+
+		seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+
+		validnsps[0] = NameStr(seqam->seqamname);
+		validnsps[1] = NULL;
+
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+
+		(void) am_reloptions(seqam->seqamreloptions, reloptions, true);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		ReleaseSysCache(tuple);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -670,6 +700,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3298,7 +3329,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4283,6 +4314,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4323,8 +4357,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW:
 			msg = _("\"%s\" is not a table or view");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9270,7 +9304,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqam->seqamreloptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index ab13be2..a69ad61 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2112,7 +2112,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index efa4be1..3cea360 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 76b63af..070234f 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3450,7 +3450,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3463,7 +3465,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e032142..7ef136e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1590,7 +1590,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1601,7 +1603,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e71d926..60b855a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3542,7 +3543,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3564,7 +3591,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3572,11 +3623,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3635,7 +3709,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0a55db4..375c02c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -28,6 +28,7 @@
 
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -415,6 +416,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = serial_seqam;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 78bfd34..ce712d8 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -963,7 +963,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -996,7 +997,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 5bb03dd..ef66817 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1061,10 +1061,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index e745006..168bee7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -429,6 +431,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	Oid			amoptions = InvalidOid;
 
 	relation->rd_options = NULL;
 
@@ -437,10 +440,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_am->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqam->seqamreloptions;
+			break;
 		default:
 			return;
 	}
@@ -452,8 +460,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_am->amoptions : InvalidOid);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1056,11 +1063,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1535,6 +1545,36 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	Form_pg_seqam	amform;
+
+	/*
+	 * Make a copy of the pg_seqam entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(CacheMemoryContext,
+												sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+
+	/*
+	 * Initialize sequence AM info struct. (It's left as zeroes, and filled
+	 * on-the-fly when used.)
+	 */
+	rel->rd_seqaminfo = (RelationSeqAmInfo *)
+		MemoryContextAllocZero(CacheMemoryContext,
+							   sizeof(RelationSeqAmInfo));
+}
 
 /*
  *		formrdesc
@@ -2021,6 +2061,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 		pfree(relation->rd_indextuple);
 	if (relation->rd_am)
 		pfree(relation->rd_am);
+	if (relation->rd_seqam)
+		pfree(relation->rd_seqam);
+	if (relation->rd_seqaminfo)
+		pfree(relation->rd_seqaminfo);
 	if (relation->rd_indexcxt)
 		MemoryContextDelete(relation->rd_indexcxt);
 	if (relation->rd_rulescxt)
@@ -4840,6 +4884,8 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqam == NULL);
+			Assert(rel->rd_seqaminfo == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index f58e1ce..04cc7e8 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_range.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_seclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
@@ -655,6 +656,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAMNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_seqamname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAMOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5f71ded..78bc08b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2894,6 +2895,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"serial_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence access method for SERIAL and BIGSERIAL column types."),
+			gettext_noop("Defaults to 'local' sequence access method."),
+			GUC_IS_NAME
+		},
+		&serial_seqam,
+		"local",
+		check_serial_seqam, NULL, NULL
+	},
+
+	{
 		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
 			NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 110983f..a90637b 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -512,6 +512,7 @@
 #default_tablespace = ''		# a tablespace name, '' uses the default
 #temp_tablespaces = ''			# a list of tablespace names, '' uses
 					# only default tablespace
+#serial_sequenceam = 'local'	# default sequence access method for SERIAL
 #check_function_bodies = on
 #default_transaction_isolation = 'read committed'
 #default_transaction_read_only = off
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index dccb472..b0ee6e5 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "libpq/libpq-fs.h"
@@ -4534,6 +4535,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4585,6 +4587,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4626,6 +4629,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4667,6 +4671,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4708,6 +4713,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4747,6 +4753,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4785,6 +4792,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4823,6 +4831,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4861,6 +4870,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4898,6 +4908,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4931,6 +4942,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4959,6 +4971,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4997,6 +5010,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5049,6 +5063,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5113,6 +5128,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -14804,7 +14823,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14884,6 +14904,37 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.5 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -14924,6 +14975,13 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14990,6 +15048,9 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15006,16 +15067,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.5 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15028,14 +15102,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 4c796ad..3d2b66a 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -222,6 +222,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_clas) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 04d769e..a01f319 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1401,30 +1401,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1468,13 +1444,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1803,6 +1821,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1812,12 +1832,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index e7b6bb5..7523973 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -270,8 +270,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
-				 bool validate);
+extern bytea *am_reloptions(RegProcedure amoptions, Datum reloptions,
+						   bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..5040118
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,69 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+extern char *serial_seqam;
+
+extern HeapTuple seqam_init(Relation seqrel, HeapTuple tuple,
+							int64 restart_value, bool restart_requested,
+							bool is_init);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern ArrayType * seqam_get_state(Relation seqrel, SequenceHandle *seqh);
+extern void seqam_set_state(Relation seqrel, SequenceHandle *seqh,
+					 ArrayType *statearr);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_swap_tuple(SequenceHandle *seqh, HeapTuple newtup);
+extern void sequence_start_update(SequenceHandle *seqh, bool do_wal);
+extern void sequence_apply_update(SequenceHandle *seqh, bool do_wal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_local_set_state(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index e6ac394..ce0ba57 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 71e0010..b348a58 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAMNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAMOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a1e2442..7330e8d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1846,6 +1846,10 @@ 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 1009 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 1009" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -5172,6 +5176,20 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 17 "2281 2281 20 16 16" _null_ _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_get_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 1009 "2281 2281" _null_ _null_ _null_ _null_ _null_ seqam_local_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_set_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 1009" _null_ _null_ _null_ _null_ _null_ seqam_local_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, is_temporary, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 DESCR("list objects dropped by the current command");
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..1cbfc27
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	4066
+
+CATALOG(pg_seqam,4066)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamgetstate;		/* dump state, used by pg_dump */
+	regproc		seqamsetstate;		/* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						7
+#define Anum_pg_seqam_seqamname				1
+#define Anum_pg_seqam_seqamreloptions		2
+#define Anum_pg_seqam_seqaminit				3
+#define Anum_pg_seqam_seqamalloc			4
+#define Anum_pg_seqam_seqamsetval			5
+#define Anum_pg_seqam_seqamgetstate			6
+#define Anum_pg_seqam_seqamsetstate			7
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 6022 (  local		seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_get_state seqam_local_set_state));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 44862bb..bed8d7d 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -30,9 +30,9 @@ typedef struct FormData_pg_sequence
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
 	bool		is_called;
+	bytea		amdata;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -48,12 +48,12 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 #define SEQ_COL_MAXVALUE		5
 #define SEQ_COL_MINVALUE		6
 #define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_CYCLE			8
+#define SEQ_COL_CALLED			9
+#define SEQ_COL_AMDATA			10
 
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_AMDATA
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +72,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index f269c63..cb621b1 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 556c1c5..10e2a1e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2077,8 +2077,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2086,8 +2088,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ff78b70..1ac3ef7 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -410,6 +410,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_serial_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 9e17d87..c61bc41 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -65,6 +66,18 @@ typedef struct RelationAmInfo
 
 
 /*
+ * Cached lookup information for the frequently used sequence access method
+ * functions, defined by the pg_seqam row associated with a sequencerelation.
+ */
+typedef struct RelationSeqAmInfo
+{
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+} RelationSeqAmInfo;
+
+
+/*
  * Here are the contents of a relation cache entry.
  */
 
@@ -125,6 +138,10 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	/* These are non-NULL only for a sequence relation */
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+	RelationSeqAmInfo *rd_seqaminfo; /* lookup info for funcs found in pg_seqam */
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
@@ -132,7 +149,7 @@ typedef struct RelationData
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 6634099..b16cc85 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -80,6 +80,8 @@ enum SysCacheIdentifier
 	REPLORIGIDENT,
 	REPLORIGNAME,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TRFOID,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index eb0bc88..02a2e6d 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -124,6 +124,7 @@ pg_range|t
 pg_replication_origin|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3dc5af9 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | f         | \x0000000000000000
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | t         | \x1f00000000000000
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 7eb9261..8f0b33d 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -106,9 +106,9 @@ SELECT table_name, column_name, is_updatable
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
  ro_view19  | is_called     | NO
+ ro_view19  | amdata        | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
  ro_view20  | a             | NO
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..0a54569 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
#77Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Alvaro Herrera (#76)
Re: Sequence Access Method WIP

On 05/13/2015 07:10 AM, Alvaro Herrera wrote:

Heikki, do you have time to go through this at this point?

I'm afraid I won't :-(. I did intend to, but looking at the calendar, I
won't have the time to review this thoroughly enough to commit. Sorry.

I haven't looked at the CREATE/DROP ACCESS METHOD FOR SEQUENCE syntax
patch at all yet.

We discussed using a single amdata column vs. any number of am-specific
columns. We settled on amdata, but I'm still not 100% convinced that's
the best approach. Just as a data point, this removes the log_cnt field
and moves it into amdata in a non-human-readable format. So for someone
who only uses the local seqam, this just makes things slightly worse.
For more complicated seqam's, it would be even more important to display
the state in a human-readable format. Perhaps it's OK that each seqam
provides its own functions or similar to do that, but I'd like to
revisit that decision.

I still don't like the serial_sequenceam GUC. Not sure what to do
instead. Needs some thought.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#78Simon Riggs
simon@2ndQuadrant.com
In reply to: Heikki Linnakangas (#77)
Re: Sequence Access Method WIP

On 13 May 2015 at 11:56, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

On 05/13/2015 07:10 AM, Alvaro Herrera wrote:

Heikki, do you have time to go through this at this point?

I'm afraid I won't :-(. I did intend to, but looking at the calendar, I
won't have the time to review this thoroughly enough to commit. Sorry.

I haven't looked at the CREATE/DROP ACCESS METHOD FOR SEQUENCE syntax
patch at all yet.

We discussed using a single amdata column vs. any number of am-specific
columns. We settled on amdata, but I'm still not 100% convinced that's the
best approach. Just as a data point, this removes the log_cnt field and
moves it into amdata in a non-human-readable format. So for someone who
only uses the local seqam, this just makes things slightly worse. For more
complicated seqam's, it would be even more important to display the state
in a human-readable format. Perhaps it's OK that each seqam provides its
own functions or similar to do that, but I'd like to revisit that decision.

I still don't like the serial_sequenceam GUC. Not sure what to do instead.
Needs some thought.

This has had around 2 years of thought at this point. I don't agree it
needs more thought.

There is one clear use case for this and it is of benefit to many
distributed architectures.

I don't see what calamity will occur if we commit this. If you don't want a
sequence AM, don't ever use this.

--
Simon Riggs http://www.2ndQuadrant.com/
<http://www.2ndquadrant.com/&gt;
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#79Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#77)
Re: Sequence Access Method WIP

On 13/05/15 12:56, Heikki Linnakangas wrote:

On 05/13/2015 07:10 AM, Alvaro Herrera wrote:

Heikki, do you have time to go through this at this point?

I'm afraid I won't :-(. I did intend to, but looking at the calendar, I
won't have the time to review this thoroughly enough to commit. Sorry.

We discussed using a single amdata column vs. any number of am-specific
columns. We settled on amdata, but I'm still not 100% convinced that's
the best approach. Just as a data point, this removes the log_cnt field
and moves it into amdata in a non-human-readable format. So for someone
who only uses the local seqam, this just makes things slightly worse.
For more complicated seqam's, it would be even more important to display
the state in a human-readable format. Perhaps it's OK that each seqam
provides its own functions or similar to do that, but I'd like to
revisit that decision.

I do think it's ok for seqam to provide functions that can give you the
data in human readable form.

I think main argument against custom human readable columns is that it
will kill any possibility to have common storage for sequences.

There is also additional complexity in the API required for that.

The performance implications are probably small as one could still
define opaque bytea column and store the data same way a now.

I still don't like the serial_sequenceam GUC. Not sure what to do
instead. Needs some thought.

I think it would be ok if this issue was solved by follow-up patch at
later time.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#80Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Simon Riggs (#78)
Re: Sequence Access Method WIP

On 05/13/2015 02:12 PM, Simon Riggs wrote:

This has had around 2 years of thought at this point. I don't agree it
needs more thought.

Noted.

There is one clear use case for this and it is of benefit to many
distributed architectures.

Right. What's your point?

I don't see what calamity will occur if we commit this. If you don't want a
sequence AM, don't ever use this.

I'd like the API to be good for its purpose. Also, I did mention that
the the current patch makes the situation slightly worse for people who
never use this: it makes the log_cnt field non human-readable. That's a
really minor thing, but it shows that it *does* matter how this is
implemented, even if you only ever use the local seq AM.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#81Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#80)
Re: Sequence Access Method WIP

On 13/05/15 14:15, Heikki Linnakangas wrote:

I don't see what calamity will occur if we commit this. If you don't
want a
sequence AM, don't ever use this.

I'd like the API to be good for its purpose. Also, I did mention that
the the current patch makes the situation slightly worse for people who
never use this: it makes the log_cnt field non human-readable. That's a
really minor thing, but it shows that it *does* matter how this is
implemented, even if you only ever use the local seq AM.

It definitely does matter.

I don't think we'll find perfect compromise here though, you can either
do it one way or the other. Trust me it does not make me happy either, I
like perfect solutions too, but when there is lack of perfect solution I
prefer the simpler one.

Both of the solutions have drawbacks
- amdata has opaque blob which does not store data in user visible way
but that can be worked around by providing function that shows it in
human readable way (and the dump function for many sequence types
actually does that).
- multiple columns basically kill any future ability to unify the
storage for sequences and also adds complexity, especially around alter
table (since it means drop/add column and stuff)

But I already wrote both versions anyway so from that perspective it
does not matter much which part we merge.

(As a side-node I would have preferred to have this discussion earlier
than 2 days before feature freeze because the current implementation is
something that we agreed on several months ago so there was plenty of
time to revisit that decision.)

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#82Petr Jelinek
petr@2ndquadrant.com
In reply to: Alvaro Herrera (#76)
3 attachment(s)
Re: Sequence Access Method WIP

On 13/05/15 06:10, Alvaro Herrera wrote:

I rebased your #1 to current master; attached. It builds and passes

Thanks

regression test, but I didn't check any further. Doc build fails with:

$ make check
onsgmls -wall -wno-unused-param -wno-empty -wfully-tagged -D . -D /pgsql/source/master/doc/src/sgml -s /pgsql/source/master/doc/src/sgml/postgres.sgml
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:185:3:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:185:44:E: document type does not allow element "LITERAL" here; missing one of "REMARK", "SYNOPSIS", "LITERALLAYOUT", "PROGRAMLISTING", "SCREEN", "PARA", "SIMPARA", "BRIDGEHEAD" start-tag
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:185:54:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:186:53:E: element "STRUCT" undefined
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:186:71:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:187:31:E: document type does not allow element "FUNCTION" here; missing one of "REMARK", "SYNOPSIS", "LITERALLAYOUT", "PROGRAMLISTING", "SCREEN", "PARA", "SIMPARA", "BRIDGEHEAD" start-tag
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:187:55:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:189:12:E: document type does not allow element "FUNCTION" here; missing one of "REMARK", "SYNOPSIS", "LITERALLAYOUT", "PROGRAMLISTING", "SCREEN", "PARA", "SIMPARA", "BRIDGEHEAD" start-tag
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:189:38:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:190:28:E: document type does not allow element "FUNCTION" here; missing one of "REMARK", "SYNOPSIS", "LITERALLAYOUT", "PROGRAMLISTING", "SCREEN", "PARA", "SIMPARA", "BRIDGEHEAD" start-tag
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:190:54:E: character data is not allowed here
onsgmls:/pgsql/source/master/doc/src/sgml/seqam.sgml:191:8:E: end tag for element "PARA" which is not open
Makefile:320: recipe for target 'check' failed
make: *** [check] Error 1

Missing <para>, attached fixed version with your rebase and one of the
commits from Heikki that I missed last time.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-seqam-v10.patchbinary/octet-stream; name=0001-seqam-v10.patchDownload
>From a18d165637e52e5fbbb5316387ba6ccad4b54c71 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Wed, 13 May 2015 11:05:52 +0200
Subject: [PATCH 1/3] seqam v10

---
 doc/src/sgml/catalogs.sgml                    |   94 ++
 doc/src/sgml/filelist.sgml                    |    1 +
 doc/src/sgml/postgres.sgml                    |    1 +
 doc/src/sgml/ref/alter_sequence.sgml          |   55 ++
 doc/src/sgml/ref/create_sequence.sgml         |   25 +
 doc/src/sgml/seqam.sgml                       |  290 ++++++
 src/backend/access/Makefile                   |    3 +-
 src/backend/access/common/reloptions.c        |   12 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqam.c           |  270 ++++++
 src/backend/access/sequence/seqlocal.c        |  383 ++++++++
 src/backend/bootstrap/bootparse.y             |    1 +
 src/backend/catalog/Makefile                  |    2 +-
 src/backend/catalog/heap.c                    |   16 +-
 src/backend/catalog/objectaddress.c           |    1 +
 src/backend/catalog/toasting.c                |    1 +
 src/backend/commands/cluster.c                |    1 +
 src/backend/commands/createas.c               |    3 +-
 src/backend/commands/indexcmds.c              |    2 +-
 src/backend/commands/sequence.c               | 1251 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |   59 +-
 src/backend/commands/typecmds.c               |    3 +-
 src/backend/commands/view.c                   |    3 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   78 +-
 src/backend/parser/parse_utilcmd.c            |    3 +
 src/backend/tcop/utility.c                    |    6 +-
 src/backend/utils/cache/catcache.c            |    6 +-
 src/backend/utils/cache/relcache.c            |   60 +-
 src/backend/utils/cache/syscache.c            |   23 +
 src/backend/utils/misc/guc.c                  |    1 +
 src/bin/pg_dump/pg_dump.c                     |  115 ++-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/psql/describe.c                       |  112 ++-
 src/include/access/reloptions.h               |    4 +-
 src/include/access/seqam.h                    |   67 ++
 src/include/catalog/heap.h                    |    1 +
 src/include/catalog/indexing.h                |    5 +
 src/include/catalog/pg_proc.h                 |   18 +
 src/include/catalog/pg_seqam.h                |   74 ++
 src/include/commands/sequence.h               |   12 +-
 src/include/commands/tablecmds.h              |    2 +-
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/utils/rel.h                       |   19 +-
 src/include/utils/syscache.h                  |    2 +
 src/test/regress/expected/sanity_check.out    |    1 +
 src/test/regress/expected/sequence.out        |   35 +-
 src/test/regress/expected/updatable_views.out |    2 +-
 src/test/regress/sql/sequence.sql             |   10 +-
 50 files changed, 2565 insertions(+), 602 deletions(-)
 create mode 100644 doc/src/sgml/seqam.sgml
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqam.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqam.h
 create mode 100644 src/include/catalog/pg_seqam.h

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 2756652..8faa87b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -259,6 +259,11 @@
      </row>
 
      <row>
+      <entry><link linkend="catalog-pg-seqam"><structname>pg_seqam</structname></link></entry>
+      <entry>sequence access methods</entry>
+     </row>
+
+     <row>
       <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
       <entry>dependencies on shared objects</entry>
      </row>
@@ -5659,6 +5664,95 @@
   </table>
  </sect1>
 
+ <sect1 id="catalog-pg-seqam">
+  <title><structname>pg_seqam</structname></title>
+
+  <indexterm zone="catalog-pg-seqam">
+   <primary>pg_seqam</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_seqam</structname> stores information about
+   sequence access methods. There is one row for each sequence access method
+   installed on the system.
+  </para>
+
+  <table>
+   <title><structname>pg_seqam</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+
+     <row>
+      <entry><structfield>oid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry></entry>
+      <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamreloptions</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to parse and validate <structfield>reloptions</> for the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqaminit</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function called during initialization or <command>RESET</command> of a sequence</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamalloc</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry><quote>Allocate new sequence id</quote> function</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetval</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function implementing <function>setval()</function> interface</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamgetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to dump current state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to restore a dumped state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="catalog-pg-shdepend">
   <title><structname>pg_shdepend</structname></title>
 
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 6268d54..72daef7 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 4a45138..7dfe877 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -250,6 +250,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..b7379d9
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,290 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_seqam</structname> system catalog (see
+   <xref linkend="catalog-pg-seqam">).  The contents of a
+   <structname>pg_seqam</structname> row is the name of the access method
+   and are references to
+   <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>
+   entries that identify the functions provided by the access method.
+   functions supplied by the access method.
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The behaviour of a sequence access method is defined by a set of functions.
+   These functions are implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+HeapTuple
+seqam_init (Relation seqrel, HeapTuple tuple, int64 restart_value,
+            bool restart_requested, bool is_init);
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   <literal>tuple</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the updated
+   <structname>pg_sequence</structname>. If the tuple does not have to be
+   updated returning the input <literal>tuple</> is acceptable.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+seqam_reloptions (ArrayType *reloptions, bool validate);
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value, which will be then sent
+   to the <function>seqam_init</> and stored in the catalog.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+seqam_alloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+             int64 *last);
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+bool
+seqam_setval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+ArrayType *
+seqam_get_state (Relation seqrel, SequenceHandle *seqh);
+</programlisting>
+   Dump the current state of the sequence. The return value is one dimensional
+   <literal>TEXT</> array containing list of key/value pairs it the form
+   <literal>{'key1', 'value1', 'key2', 'value2'}</>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void
+seqam_set_state (Relation seqrel, SequenceHandle *seqh, ArrayType *statearr);
+</programlisting>
+   Restore state of the sequence based on the key/value pairs defined in
+   the <literal>statearr</> in <literal>{'key1', 'value1', 'key2', 'value2'}</>
+   form. The <literal>statearr</> is defined as <literal>TEXT[]</> in SQL. This
+   function must accept output of the <function>seqam_get_state()</> function.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid relid, SequenceHandle *seqh);
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh);
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+HeapTuple
+sequence_read_tuple (SequenceHandle *seqh);
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+HeapTuple
+sequence_swap_tuple (SequenceHandle *seqh, HeapTuple *newtup);
+</programlisting>
+   Changes the working tuple to the <literal>newtup</>. This does not change
+   the sequence itself, only the state of the <structname>SequenceHandle</>.
+   To save the tuple the <function>sequence_*_update()</> sequence has to be
+   called. The tuple should be released by calling
+   <function>sequence_release_tuple</>. Note that you can't call this function
+   without calling <function>sequence_read_tuple()</> first.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal);
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_apply_update(SequenceHandle *seqh, bool do_wal);
+</programlisting>
+   Save the modified sequence tuple indicating if the change should be WAL
+   logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh, HeapTuple newtuple);
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_apply_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh);
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_tuple</> and/or
+   added by <function>sequence_swap_tuple</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh);
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_apply_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors);
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname);
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..9fcb9b2 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree \
+	      rmgrdesc sequence spgist transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 8176b6a..9543503 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -829,7 +829,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -860,7 +861,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+			options = am_reloptions(amoptions, datum, false);
+			break;
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1308,14 +1312,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
 	FmgrInfo	flinfo;
 	FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..54d6e9d
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,270 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amdata.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAM data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+	procedure = &seqrel->rd_seqaminfo->pname; \
+	if (!OidIsValid(procedure->fn_oid)) \
+	{ \
+		RegProcedure	procOid = seqrel->rd_seqam->pname; \
+		if (RegProcedureIsValid(procOid)) \
+			fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+		else if (!missing_ok) \
+			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+	} \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Method API
+ *
+ *  INTERFACE ROUTINES
+ *		seqam_init			- initialize sequence, also used for resetting
+ *		seqam_alloc			- allocate a new range of values for the sequence
+ *		seqam_setval		- implements the setval SQL interface
+ *		seqam_get_state		- dump sequence state (for pg_dump)
+ *		seqam_set_state		- restore sequence state (for pg_dump)
+ *
+ * Additionally, the am_reloptions interface is used for parsing reloptions
+ * that can be used for passing AM specific options.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+HeapTuple
+seqam_init(Relation seqrel, HeapTuple tuple, int64 restart_value,
+		   bool restart_requested, bool is_init)
+{
+	FmgrInfo	procedure;
+	Datum		ret;
+
+	fmgr_info(seqrel->rd_seqam->seqaminit, &procedure);
+
+	/*
+	 * Have the seqam's proc do its work.
+	 */
+	ret = FunctionCall5(&procedure,
+						PointerGetDatum(seqrel),
+						PointerGetDatum(tuple),
+						Int64GetDatum(restart_value),
+						BoolGetDatum(restart_requested),
+						BoolGetDatum(is_init));
+
+	return (HeapTuple) DatumGetPointer(ret);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+			int64 *last)
+{
+	FmgrInfo   *procedure;
+	Datum		ret;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+	/*
+	 * have the seqam's alloc proc do its work.
+	 */
+	ret = FunctionCall4(procedure,
+						PointerGetDatum(seqrel),
+						PointerGetDatum(seqh),
+						Int64GetDatum(nrequested),
+						PointerGetDatum(last));
+	return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	FmgrInfo   *procedure;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+	if (!OidIsValid(procedure->fn_oid))
+		return;
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	FunctionCall3(procedure,
+				  PointerGetDatum(seqrel),
+				  PointerGetDatum(seqh),
+				  Int64GetDatum(new_value));
+}
+
+/*
+ * seqam_get_state - pg_dump support
+ */
+ArrayType *
+seqam_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	FmgrInfo	procedure;
+	Datum		statearr;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamgetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	statearr = FunctionCall2(&procedure,
+							 PointerGetDatum(seqrel),
+							 PointerGetDatum(seqh));
+
+	return (ArrayType *) DatumGetPointer(statearr);
+}
+
+/*
+ * seqam_set_state - restore from pg_dump
+ */
+void
+seqam_set_state(Relation seqrel, SequenceHandle *seqh, ArrayType *statearr)
+{
+	FmgrInfo	procedure;
+
+	Assert(RelationIsValid(seqrel));
+	Assert(PointerIsValid(seqrel->rd_seqam));
+	Assert(OidIsValid(seqrel->rd_rel->relam));
+
+	fmgr_info(seqrel->rd_seqam->seqamsetstate, &procedure);
+
+	/*
+	 * have the seqam's setval proc do its work.
+	 */
+	FunctionCall3(&procedure,
+				  PointerGetDatum(seqrel),
+				  PointerGetDatum(seqh),
+				  PointerGetDatum(statearr));
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..a891bd8
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,383 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+/* Definition of additional columns for local sequence. */
+typedef struct LocalAmdata
+{
+	int64           log_cnt;
+} LocalAmdata;
+
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum		reloptions = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	HeapTuple	tuple = (HeapTuple) PG_GETARG_POINTER(1);
+	int64		restart_value = PG_GETARG_INT64(2);
+	bool		restart_requested = PG_GETARG_BOOL(3);
+	bool		is_init = PG_GETARG_BOOL(4);
+	bool		isnull;
+	Datum		amdata;
+	TupleDesc	tupDesc = RelationGetDescr(seqrel);
+	Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(tuple);
+	LocalAmdata *localseq;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seq->last_value = restart_value;
+		seq->is_called = false;
+	}
+
+	amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+						 &isnull);
+
+	/* Make sure amdata is filled. */
+	if (isnull)
+	{
+		Datum			values[SEQ_COL_LASTCOL];
+		bool			nulls[SEQ_COL_LASTCOL];
+		bool			replace[SEQ_COL_LASTCOL];
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(LocalAmdata));
+		HeapTuple		newtup;
+
+		memset(replace, false, sizeof(replace));
+		memset(nulls, false, sizeof(nulls));
+		memset(values, 0, sizeof(values));
+
+		replace[SEQ_COL_AMDATA - 1] = true;
+		nulls[SEQ_COL_AMDATA - 1] = false;
+
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(LocalAmdata));
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replace);
+
+		/* Don't leak memory. */
+		heap_freetuple(tuple);
+		tuple = newtup;
+
+		amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+							 &isnull);
+	}
+
+	localseq = (LocalAmdata *)
+		VARDATA_ANY(DatumGetByteaP(amdata));
+
+	/* We always reset the log_cnt. */
+	localseq->log_cnt = 0;
+
+	PG_RETURN_POINTER(tuple);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	int64	    nrequested = PG_GETARG_INT64(2);
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	FormData_pg_sequence *seq;
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	LocalAmdata *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+	next = result = seq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = seq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	sequence_start_update(seqh, logit);
+	if (logit)
+	{
+		seq->last_value = next;
+		seq->is_called = true;
+		localseq->log_cnt = 0;
+
+		sequence_apply_update(seqh, true);
+	}
+
+	/* Now update sequence tuple to the intended final state */
+	seq->last_value = *last;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = log;		/* how much is logged */
+
+	sequence_apply_update(seqh, false);
+	sequence_finish_update(seqh);
+
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	int64		next = PG_GETARG_INT64(2);
+	FormData_pg_sequence *seq;
+	LocalAmdata *localseq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+	seq->last_value = next;		/* last fetched number */
+	seq->is_called = true;
+	localseq->log_cnt = 0;		/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_apply_update(seqh, true);
+	sequence_finish_update(seqh);
+	sequence_release_tuple(seqh);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+Datum
+seqam_local_get_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	Datum			datums[4];
+	Datum			val;
+	FormData_pg_sequence *seq;
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+	datums[0] = CStringGetTextDatum("last_value");
+	val = DirectFunctionCall1(int8out, Int64GetDatum(seq->last_value));
+	datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+	datums[2] = CStringGetTextDatum("is_called");
+	val = DirectFunctionCall1(boolout, BoolGetDatum(seq->is_called));
+	datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+	sequence_release_tuple(seqh);
+
+	PG_RETURN_ARRAYTYPE_P(construct_array(datums, 4, TEXTOID, -1, false, 'i'));
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+Datum
+seqam_local_set_state(PG_FUNCTION_ARGS)
+{
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	ArrayType	   *statearr = PG_GETARG_ARRAYTYPE_P(2);
+	FormData_pg_sequence *seq;
+	int64			last_value = 0;
+	bool			is_called = false;
+	bool			last_value_found = false,
+					is_called_found = false;
+	Datum		   *datums;
+	int				count;
+	int				i;
+
+	Assert(ARR_ELEMTYPE(statearr) == TEXTOID && ARR_NDIM(statearr) == 1 &&
+		   (ARR_DIMS(statearr)[0]) % 2 == 0 && !ARR_HASNULL(statearr));
+
+	deconstruct_array(statearr,
+					  TEXTOID, -1, false, 'i',
+					  &datums, NULL, &count);
+	count /= 2;
+	for (i = 0; i < count; ++i)
+	{
+		char   *key,
+			   *val;
+
+		key = TextDatumGetCString(datums[i * 2]);
+		val = TextDatumGetCString(datums[i * 2 + 1]);
+
+		if (pg_strcasecmp(key, "last_value") == 0)
+		{
+			last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+														CStringGetDatum(val)));
+			last_value_found = true;
+		}
+		else if (pg_strcasecmp(key, "is_called") == 0)
+		{
+			is_called = DatumGetBool(DirectFunctionCall1(boolin,
+														CStringGetDatum(val)));
+			is_called_found = true;
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for local sequence",
+							key)));
+	}
+
+	if (!last_value_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("last_value is required parameter for local sequence")));
+
+	if (!is_called_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("is_called is required parameter for local sequence")));
+
+
+	seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	sequence_check_range(last_value, seq->min_value, seq->max_value, "last_value");
+
+	sequence_start_update(seqh, true);
+	seq->last_value = last_value;
+	seq->is_called = is_called;
+	sequence_apply_update(seqh, true);
+	sequence_finish_update(seqh);
+	sequence_release_tuple(seqh);
+
+	PG_FREE_IF_COPY(statearr, 2);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 6e563b6..1c85569 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -252,6 +252,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 37d05d1..d1043ba 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_statistic.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_pltemplate.h \
-	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+	pg_authid.h pg_auth_members.h pg_seqam.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 \
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d04e94d..1fd46a6 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -87,6 +87,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -847,6 +848,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -920,6 +922,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1033,6 +1036,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1261,6 +1265,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1273,7 +1278,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1328,6 +1334,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = SeqAccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index a1f8ada..cbb0f5f 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index c99d353..0b35046 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -290,6 +290,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 3febdd5..e2c1990 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -678,6 +678,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e8f0d79..f7bfca0 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 351d48e..bcf1065 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -527,7 +527,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 80f5553..d2f52bb 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,6 +14,9 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
@@ -22,8 +25,10 @@
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +41,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,6 +81,16 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	HeapTupleData tup;
+	Page		temppage;
+	bool		inupdate;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -91,13 +101,11 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
 
 
 /*
@@ -111,6 +119,7 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
@@ -147,6 +156,11 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
+	if (seq->accessMethod)
+		seqamid = get_seqam_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
 	/*
 	 * Create relation (and fill value[] and null[] for the tuple)
 	 */
@@ -207,11 +221,6 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "cache_value";
 				value[i - 1] = Int64GetDatumFast(new.cache_value);
 				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
 			case SEQ_COL_CYCLE:
 				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
 				coldef->colname = "is_cycled";
@@ -222,6 +231,12 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "is_called";
 				value[i - 1] = BoolGetDatum(false);
 				break;
+			case SEQ_COL_AMDATA:
+				coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+				coldef->colname = "amdata";
+				null[i - 1] = true;
+				value[i - 1] = (Datum) 0;
+				break;
 		}
 		stmt->tableElts = lappend(stmt->tableElts, coldef);
 	}
@@ -229,12 +244,13 @@ DefineSequence(CreateSeqStmt *seq)
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
@@ -243,6 +259,7 @@ DefineSequence(CreateSeqStmt *seq)
 
 	/* now initialize the sequence's data */
 	tuple = heap_form_tuple(tupDesc, value, null);
+	tuple = seqam_init(rel, tuple, new.start_value, false, true);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +271,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +285,55 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
 	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
+	 * Tell AM to reset the sequence.
+	 * This fakes the ALTER SEQUENCE RESTART command from the
+	 * Sequence AM perspective.
 	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tuple = seqam_init(seqrel, tuple, seq->start_value, true, false);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
 }
 
 /*
@@ -361,7 +376,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +396,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -406,19 +411,23 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence new;
 	List	   *owned_by;
 	ObjectAddress address;
+	List	   *seqoptions;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +435,173 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	seqoptions = stmt->options;
+	init_params(seqoptions, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	oldamid = seqrel->rd_rel->relam;
+	if (stmt->accessMethod)
+		seqamid = get_seqam_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(seqoptions, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		Form_pg_seqam	seqam;
+		HeapTuple       seqamtup;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = seqam_alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		seqam = (Form_pg_seqam) GETSTRUCT(seqamtup);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		validnsps[0] = NameStr(seqam->seqamname);
+		validnsps[1] = NULL;
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		PageSetLSN(page, recptr);
-	}
+		(void) am_reloptions(seqam->seqamreloptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-	END_CRIT_SECTION();
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-	UnlockReleaseBuffer(buf);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
+
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
+
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/* Let the new sequence AM initialize. */
+		tuple = seqam_init(seqrel, tuple, restart_value, true, true);
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * See ResetSequence for why we do this.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		/*
+		 * Insert the modified tuple into the new storage file.
+		 */
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										SeqAccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = SeqAccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+	else
+	{
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
+
+		/* Let sequence AM update the tuple. */
+		tuple = seqam_init(seqrel, tuple, restart_value, restart_requested, false);
+		sequence_swap_tuple(&seqh, tuple);
+		sequence_start_update(&seqh, true);
+		sequence_apply_update(&seqh, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +642,24 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +684,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam_alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +701,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for
-	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
-	 * no need to assign xids subxacts, that'll already trigger a appropriate
-	 * wait.  (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +712,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +773,24 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,92 +807,27 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam_setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
-}
-
-/*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
+	last_used_seq = elm;
 
-	do_setval(relid, next, true);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
 /*
  * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
 setval3_oid(PG_FUNCTION_ARGS)
@@ -969,12 +835,66 @@ setval3_oid(PG_FUNCTION_ARGS)
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
 	bool		iscalled = PG_GETARG_BOOL(2);
+	ArrayType  *statearr;
+	Datum		datums[4];
+	Datum		val;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
 
-	do_setval(relid, next, iscalled);
-
-	PG_RETURN_INT64(next);
-}
-
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
+
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Convert the data into 'local' sequence dump format and call restore API. */
+	datums[0] = CStringGetTextDatum("last_value");
+	val = DirectFunctionCall1(int8out, Int64GetDatum(next));
+	datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+	datums[2] = CStringGetTextDatum("is_called");
+	val = DirectFunctionCall1(boolout, BoolGetDatum(iscalled));
+	datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+	statearr = construct_array(datums, 4, TEXTOID, -1, false, 'i');
+
+	seqam_set_state(seqh.rel, &seqh, statearr);
+
+	pfree(statearr);
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
+
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
+
+	sequence_close(&seqh);
+
+	PG_RETURN_INT64(next);
+}
 
 /*
  * Open the sequence and acquire AccessShareLock if needed
@@ -1034,11 +954,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1009,56 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->temppage = NULL;
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(seqh->temppage == NULL && !seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
 
-	page = BufferGetPage(*buf);
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1068,185 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
 	}
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+/*
+ * Swap the current working tuple.
+ *
+ * Note that the changes are only saved to in-memory state and will not be
+ * visible unless the sequence_*_update sequence is called.
+ */
+void
+sequence_swap_tuple(SequenceHandle *seqh, HeapTuple newtup)
+{
+	Page	page;
+
+	Assert(!seqh->inupdate && seqh->tup.t_data != NULL &&
+		   seqh->temppage == NULL);
+
+	page = BufferGetPage(seqh->buf);
+
+	seqh->temppage = PageGetTempPageCopySpecial(page);
+
+	/* Sequence tuples are always frozen. */
+	HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+	HeapTupleHeaderSetXminFrozen(newtup->t_data);
+	HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+	HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+	newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+	ItemPointerSet(&newtup->t_data->t_ctid, 0, FirstOffsetNumber);
+
+	if (PageAddItem(seqh->temppage, (Item) newtup->t_data, newtup->t_len,
+				FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+		elog(PANIC, "sequence_apply_update: failed to add item to page");
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	PageSetLSN(seqh->temppage, PageGetLSN(page));
 
-	return seq;
+	seqh->tup.t_data = newtup->t_data;
+	seqh->tup.t_len = newtup->t_len;
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+	if (do_wal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_apply_update(SequenceHandle *seqh, bool do_wal)
+{
+	Page	page;
+
+	Assert(seqh->inupdate && seqh->tup.t_data != NULL);
+
+	page = BufferGetPage(seqh->buf);
+
+	/*
+	 * If the working tuple was swapped we need to copy it to the page
+	 */
+	if (seqh->temppage != NULL)
+	{
+		PageRestoreTempPage(seqh->temppage, page);
+		seqh->temppage = NULL;
+	}
+
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (do_wal)
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate && seqh->temppage == NULL);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	if (seqh->temppage)
+	{
+		pfree(seqh->temppage);
+		seqh->temppage = NULL;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
+	{
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
+	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
+
+	Assert(BufferIsValid(seqh->buf));
+
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
+
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
+}
+
+/*
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1256,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1333,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1341,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1350,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1358,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1365,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1378,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1431,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1445,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1555,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1583,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1591,89 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *statearr;
+	SequenceHandle seqh;
+
+	sequence_open(relid, &seqh);
+
+	statearr = seqam_get_state(seqh.rel, &seqh);
+
+	sequence_close(&seqh);
+
+	Assert(ARR_ELEMTYPE(statearr) == TEXTOID && ARR_NDIM(statearr) == 1 &&
+		   (ARR_DIMS(statearr)[0]) % 2 == 0 && !ARR_HASNULL(statearr));
+
+	PG_RETURN_ARRAYTYPE_P(statearr);
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	ArrayType  *statearr = PG_GETARG_ARRAYTYPE_P(1);
+	SequenceHandle seqh;
+
+	Assert(ARR_ELEMTYPE(statearr) == TEXTOID);
+
+	/*
+	 * Do the input checks.
+	 */
+	if (ARR_NDIM(statearr) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state must be one dimensional array")));
+
+	if ((ARR_DIMS(statearr)[0]) % 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state array must have even number of elements")));
+
+	if (array_contains_nulls(statearr))
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("state array cannot contain NULLs")));
+
+	/* Call in the sequence. */
+	sequence_open(relid, &seqh);
+
+	seqam_set_state(seqh.rel, &seqh, statearr);
+
+	sequence_close(&seqh);
+
+	PG_FREE_IF_COPY(statearr, 1);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1739,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0a6b069..86c0c5f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -266,6 +266,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -446,7 +447,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -465,7 +466,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -543,13 +543,43 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		HeapTuple	tuple;
+		Form_pg_seqam seqam;
+		static char *validnsps[2];
+
+		Assert(relamid != InvalidOid);
+
+		tuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(relamid));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 relamid);
+
+		seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+
+		validnsps[0] = NameStr(seqam->seqamname);
+		validnsps[1] = NULL;
+
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+
+		(void) am_reloptions(seqam->seqamreloptions, reloptions, true);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		ReleaseSysCache(tuple);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -670,6 +700,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3298,7 +3329,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4283,6 +4314,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4323,8 +4357,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW:
 			msg = _("\"%s\" is not a table or view");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9270,7 +9304,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqam->seqamreloptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index ab13be2..a69ad61 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2112,7 +2112,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index efa4be1..3cea360 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 76b63af..070234f 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3450,7 +3450,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3463,7 +3465,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e032142..7ef136e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1590,7 +1590,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1601,7 +1603,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e71d926..60b855a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3542,7 +3543,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3564,7 +3591,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3572,11 +3623,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3635,7 +3709,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0a55db4..e4699be 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -28,6 +28,7 @@
 
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -415,6 +416,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 78bfd34..ce712d8 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -963,7 +963,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -996,7 +997,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 5bb03dd..ef66817 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1061,10 +1061,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
 		case AMOID:
 		case AMNAME:
+		case SEQAMOID:
+		case SEQAMNAME:
 
 			/*
-			 * Always do heap scans in pg_am, because it's so small there's
-			 * not much point in an indexscan anyway.  We *must* do this when
+			 * Always do heap scans in pg_am and pg_seqam, because they are
+			 * too small to benefit from an indexscan.  We *must* do this when
 			 * initially building critical relcache entries, but we might as
 			 * well just always do it.
 			 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index e745006..168bee7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -429,6 +431,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	Oid			amoptions = InvalidOid;
 
 	relation->rd_options = NULL;
 
@@ -437,10 +440,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_am->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqam->seqamreloptions;
+			break;
 		default:
 			return;
 	}
@@ -452,8 +460,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_am->amoptions : InvalidOid);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1056,11 +1063,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1535,6 +1545,36 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple		amtuple;
+	Form_pg_seqam	amform;
+
+	/*
+	 * Make a copy of the pg_seqam entry for the sequence's access method
+	 */
+	amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(amtuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 rel->rd_rel->relam);
+	amform = (Form_pg_seqam) MemoryContextAlloc(CacheMemoryContext,
+												sizeof(*amform));
+	memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+	ReleaseSysCache(amtuple);
+	rel->rd_seqam = amform;
+
+	/*
+	 * Initialize sequence AM info struct. (It's left as zeroes, and filled
+	 * on-the-fly when used.)
+	 */
+	rel->rd_seqaminfo = (RelationSeqAmInfo *)
+		MemoryContextAllocZero(CacheMemoryContext,
+							   sizeof(RelationSeqAmInfo));
+}
 
 /*
  *		formrdesc
@@ -2021,6 +2061,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 		pfree(relation->rd_indextuple);
 	if (relation->rd_am)
 		pfree(relation->rd_am);
+	if (relation->rd_seqam)
+		pfree(relation->rd_seqam);
+	if (relation->rd_seqaminfo)
+		pfree(relation->rd_seqaminfo);
 	if (relation->rd_indexcxt)
 		MemoryContextDelete(relation->rd_indexcxt);
 	if (relation->rd_rulescxt)
@@ -4840,6 +4884,8 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqam == NULL);
+			Assert(rel->rd_seqaminfo == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index f58e1ce..04cc7e8 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_range.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_seclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
@@ -655,6 +656,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		8
 	},
+	{SeqAccessMethodRelationId,	/* SEQAMNAME */
+		SeqAMNameIndexId,
+		1,
+		{
+			Anum_pg_seqam_seqamname,
+			0,
+			0,
+			0
+		},
+		4
+	},
+	{SeqAccessMethodRelationId,	/* SEQAMOID */
+		SeqAMOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		4
+	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
 		3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5f71ded..5f8ae84 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index dccb472..b0ee6e5 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "libpq/libpq-fs.h"
@@ -4534,6 +4535,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4585,6 +4587,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4626,6 +4629,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4667,6 +4671,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4708,6 +4713,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4747,6 +4753,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4785,6 +4792,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4823,6 +4831,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4861,6 +4870,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4898,6 +4908,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4931,6 +4942,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4959,6 +4971,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4997,6 +5010,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5049,6 +5063,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5113,6 +5128,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -14804,7 +14823,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -14884,6 +14904,37 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.5 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.seqamname\n"
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -14924,6 +14975,13 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14990,6 +15048,9 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15006,16 +15067,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.5 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15028,14 +15102,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90500 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 4c796ad..3d2b66a 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -222,6 +222,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_clas) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 04d769e..a01f319 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1401,30 +1401,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1468,13 +1444,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1803,6 +1821,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1812,12 +1832,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+								"FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index e7b6bb5..7523973 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -270,8 +270,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
-				 bool validate);
+extern bytea *am_reloptions(RegProcedure amoptions, Datum reloptions,
+						   bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..7baa47c
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,67 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+extern HeapTuple seqam_init(Relation seqrel, HeapTuple tuple,
+							int64 restart_value, bool restart_requested,
+							bool is_init);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+						 int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+						 int64 new_value);
+extern ArrayType * seqam_get_state(Relation seqrel, SequenceHandle *seqh);
+extern void seqam_set_state(Relation seqrel, SequenceHandle *seqh,
+					 ArrayType *statearr);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_swap_tuple(SequenceHandle *seqh, HeapTuple newtup);
+extern void sequence_start_update(SequenceHandle *seqh, bool do_wal);
+extern void sequence_apply_update(SequenceHandle *seqh, bool do_wal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_local_set_state(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index e6ac394..ce0ba57 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 71e0010..b348a58 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAMNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAMOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId		1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a1e2442..7330e8d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1846,6 +1846,10 @@ 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 1009 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 1009" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -5172,6 +5176,20 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6023 (  seqam_local_reloptions	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 17 "2281 2281 20 16 16" _null_ _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc	   PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_get_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 1009 "2281 2281" _null_ _null_ _null_ _null_ _null_ seqam_local_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_set_state	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 1009" _null_ _null_ _null_ _null_ _null_ seqam_local_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, is_temporary, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 DESCR("list objects dropped by the current command");
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..1cbfc27
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *	  definition of the system "sequence access method" relation (pg_seqam)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *		the genbki.pl script reads this file and generates .bki
+ *		information from the DATA() statements.
+ *
+ *		XXX do NOT break up DATA() statements into multiple lines!
+ *			the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_seqam definition.  cpp turns this into
+ *		typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId	4066
+
+CATALOG(pg_seqam,4066)
+{
+	NameData	seqamname;			/* access method name */
+	regproc		seqamreloptions;	/* parse AM-specific options */
+	regproc		seqaminit;			/* sequence initialization */
+	regproc		seqamalloc;			/* get next allocation of range of values function */
+	regproc		seqamsetval;		/* set value function */
+	regproc		seqamgetstate;		/* dump state, used by pg_dump */
+	regproc		seqamsetstate;		/* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *		Form_pg_seqam corresponds to a pointer to a tuple with
+ *		the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *		compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam						7
+#define Anum_pg_seqam_seqamname				1
+#define Anum_pg_seqam_seqamreloptions		2
+#define Anum_pg_seqam_seqaminit				3
+#define Anum_pg_seqam_seqamalloc			4
+#define Anum_pg_seqam_seqamsetval			5
+#define Anum_pg_seqam_seqamgetstate			6
+#define Anum_pg_seqam_seqamsetstate			7
+
+/* ----------------
+ *		initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 6022 (  local		seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_get_state seqam_local_set_state));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#endif   /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 44862bb..bed8d7d 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -30,9 +30,9 @@ typedef struct FormData_pg_sequence
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
 	bool		is_called;
+	bytea		amdata;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -48,12 +48,12 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 #define SEQ_COL_MAXVALUE		5
 #define SEQ_COL_MINVALUE		6
 #define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_CYCLE			8
+#define SEQ_COL_CALLED			9
+#define SEQ_COL_AMDATA			10
 
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL			SEQ_COL_AMDATA
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +72,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index f269c63..cb621b1 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 556c1c5..10e2a1e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2077,8 +2077,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2086,8 +2088,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 9e17d87..c61bc41 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -65,6 +66,18 @@ typedef struct RelationAmInfo
 
 
 /*
+ * Cached lookup information for the frequently used sequence access method
+ * functions, defined by the pg_seqam row associated with a sequencerelation.
+ */
+typedef struct RelationSeqAmInfo
+{
+	/* pg_seqam only */
+	FmgrInfo	seqamalloc;
+	FmgrInfo	seqamsetval;
+} RelationSeqAmInfo;
+
+
+/*
  * Here are the contents of a relation cache entry.
  */
 
@@ -125,6 +138,10 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	/* These are non-NULL only for a sequence relation */
+	Form_pg_seqam rd_seqam;		/* pg_seqam tuple for sequence's AM */
+	RelationSeqAmInfo *rd_seqaminfo; /* lookup info for funcs found in pg_seqam */
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
@@ -132,7 +149,7 @@ typedef struct RelationData
 	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 6634099..b16cc85 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -80,6 +80,8 @@ enum SysCacheIdentifier
 	REPLORIGIDENT,
 	REPLORIGNAME,
 	RULERELNAME,
+	SEQAMNAME,
+	SEQAMOID,
 	STATRELATTINH,
 	TABLESPACEOID,
 	TRFOID,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index eb0bc88..02a2e6d 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -124,6 +124,7 @@ pg_range|t
 pg_replication_origin|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3dc5af9 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | f         | \x0000000000000000
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | t         | \x1f00000000000000
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 7eb9261..8f0b33d 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -106,9 +106,9 @@ SELECT table_name, column_name, is_updatable
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
  ro_view19  | is_called     | NO
+ ro_view19  | amdata        | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
  ro_view20  | a             | NO
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..0a54569 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
-- 
1.9.1

0002-seqam-ddl-v5.patchbinary/octet-stream; name=0002-seqam-ddl-v5.patchDownload
>From 149d44f96a6da30024b23129b87363495e2736dc Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Sun, 15 Feb 2015 03:00:24 +0100
Subject: [PATCH 2/3] seqam ddl v5

---
 doc/src/sgml/catalogs.sgml           |   6 +
 doc/src/sgml/ref/allfiles.sgml       |   2 +
 doc/src/sgml/ref/create_seqam.sgml   | 163 ++++++++++++++++++
 doc/src/sgml/ref/drop_seqam.sgml     |  87 ++++++++++
 doc/src/sgml/reference.sgml          |   2 +
 src/backend/access/sequence/seqam.c  | 308 +++++++++++++++++++++++++++++++++++
 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/parser/gram.y            |  13 +-
 src/backend/tcop/utility.c           |  12 ++
 src/bin/pg_dump/common.c             |   5 +
 src/bin/pg_dump/pg_dump.c            | 158 +++++++++++++++++-
 src/bin/pg_dump/pg_dump.h            |   9 +-
 src/bin/pg_dump/pg_dump_sort.c       |  11 +-
 src/include/access/seqam.h           |   3 +
 src/include/catalog/dependency.h     |   1 +
 src/include/nodes/parsenodes.h       |   1 +
 src/include/parser/kwlist.h          |   1 +
 20 files changed, 858 insertions(+), 8 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/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 8faa87b..0a48cc8 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -5677,6 +5677,12 @@
    installed on the system.
   </para>
 
+  <para>
+   The contents of this catalog can be manipulated by
+   <command>CREATE ACCESS METHOD FOR SEQUENCES</> and
+   <command>DROP ACCESS METHOD FOR SEQUENCES</>.
+  </para>
+
   <table>
    <title><structname>pg_seqam</> Columns</title>
 
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index bf95453..2fef865 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">
@@ -117,6 +118,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..e95409f
--- /dev/null
+++ b/doc/src/sgml/ref/create_seqam.sgml
@@ -0,0 +1,163 @@
+<!--
+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> (
+    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> ,
+    GETSTATE = <replaceable class="parameter">getstate_function</replaceable>,
+    SETSTATE = <replaceable class="parameter">setstate_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">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">getstate_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">setstate_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 03020df..35c0c05 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -102,6 +102,7 @@
    &createRole;
    &createRule;
    &createSchema;
+   &createSeqAM;
    &createSequence;
    &createServer;
    &createTable;
@@ -145,6 +146,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 54d6e9d..bd9f2da 100644
--- a/src/backend/access/sequence/seqam.c
+++ b/src/backend/access/sequence/seqam.c
@@ -67,8 +67,20 @@
 #include "access/seqam.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"
 
@@ -238,6 +250,302 @@ seqam_set_state(Relation seqrel, SequenceHandle *seqh, ArrayType *statearr)
 
 
 /*
+ * 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[1] = INTERNALOID;
+			typeId[2] = INT8OID;
+			typeId[3] = BOOLOID;
+			typeId[4] = BOOLOID;
+			retTypeId = BYTEAOID;
+			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_seqamgetstate:
+			nargs = 2;
+			typeId[1] = INTERNALOID;
+			retTypeId = TEXTARRAYOID;
+			break;
+
+		case Anum_pg_seqam_seqamsetstate:
+			nargs = 3;
+			typeId[1] = INTERNALOID;
+			typeId[2] = TEXTARRAYOID;
+			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);
+}
+
+
+/*
+ * make pg_depend entries for a new pg_seqam entry
+ */
+static void
+makeSeqAMDependencies(HeapTuple tuple)
+{
+	Form_pg_seqam	seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+	ObjectAddress	myself,
+					referenced;
+
+
+	myself.classId = SeqAccessMethodRelationId;
+	myself.objectId = HeapTupleGetOid(tuple);
+	myself.objectSubId = 0;
+
+	/* Dependency on extension. */
+	recordDependencyOnCurrentExtension(&myself, false);
+
+	/* 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->seqamgetstate;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.objectId = seqam->seqamsetstate;
+	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* definition)
+{
+	char	   *seqamname = strVal(linitial(names));
+	Oid			seqamoid;
+	ListCell   *pl;
+	Relation	rel;
+	Datum		values[Natts_pg_seqam];
+	bool		nulls[Natts_pg_seqam];
+	HeapTuple	tuple;
+
+	/* 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));
+
+	/*
+	 * 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, "getstate") == 0)
+		{
+			values[Anum_pg_seqam_seqamgetstate - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamgetstate);
+		}
+		else if (pg_strcasecmp(defel->defname, "setstate") == 0)
+		{
+			values[Anum_pg_seqam_seqamsetstate - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamsetstate);
+		}
+		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_seqamgetstate - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method getstate function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamsetstate - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method setstate 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);
+
+	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 ec4ba39..d268dc5 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"
@@ -158,7 +159,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 */
 };
 
 
@@ -1270,6 +1272,10 @@ doDeletion(const ObjectAddress *object, int flags)
 			DropTransformById(object->objectId);
 			break;
 
+		case OCLASS_SEQAM:
+			RemoveSeqAMById(object->objectId);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized object class: %u",
 				 object->classId);
@@ -2399,6 +2405,9 @@ getObjectClass(const ObjectAddress *object)
 
 		case TransformRelationId:
 			return OCLASS_TRANSFORM;
+
+		case SeqAccessMethodRelationId:
+			return OCLASS_SEQAM;
 	}
 
 	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index cbb0f5f..b3ddefc 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -437,6 +437,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
 	}
 };
 
@@ -536,7 +548,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 }
 };
 
 const ObjectAddress InvalidObjectAddress =
@@ -691,6 +705,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;
@@ -942,6 +957,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 */
@@ -1002,6 +1020,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 */
@@ -2074,6 +2097,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,
@@ -3024,6 +3048,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,
@@ -3501,6 +3540,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;
@@ -4423,6 +4466,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 80a0eb6..1067ced 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -437,6 +437,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *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 7658c06..e7177c9 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -84,6 +84,7 @@ typedef enum
 
 /* XXX merge this with ObjectTypeMap? */
 static event_trigger_support_data event_trigger_support[] = {
+	{"ACCESS METHOD FOR SEQUENCES", true},
 	{"AGGREGATE", true},
 	{"CAST", true},
 	{"CONSTRAINT", true},
@@ -1102,6 +1103,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:
@@ -1164,6 +1166,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/parser/gram.y b/src/backend/parser/gram.y
index 60b855a..9e3734d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -596,7 +596,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
@@ -5194,6 +5195,14 @@ DefineStmt:
 					n->definition = list_make1(makeDefElem("from", (Node *) $5));
 					$$ = (Node *)n;
 				}
+			| CREATE ACCESS METHOD FOR SEQUENCES name AS definition
+				{
+					DefineStmt *n = makeNode(DefineStmt);
+					n->kind = OBJECT_SEQAM;
+					n->defnames = list_make1(makeString($6));
+					n->definition = $8;
+					$$ = (Node *)n;
+				}
 		;
 
 definition: '(' def_list ')'						{ $$ = $2; }
@@ -5648,6 +5657,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:
@@ -13634,6 +13644,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 ce712d8..630e28d 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"
@@ -1217,6 +1218,11 @@ ProcessUtilitySlow(Node *parsetree,
 							address = DefineCollation(stmt->defnames,
 													   stmt->definition);
 							break;
+						case OBJECT_SEQAM:
+							Assert(list_length(stmt->defnames) == 1);
+							Assert(stmt->args == NIL);
+							DefineSeqAM(stmt->defnames, stmt->definition);
+							break;
 						default:
 							elog(ERROR, "unrecognized define stmt type: %d",
 								 (int) stmt->kind);
@@ -2154,6 +2160,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_TRANSFORM:
 					tag = "DROP TRANSFORM";
 					break;
+				case OBJECT_SEQAM:
+					tag = "DROP ACCESS METHOD FOR SEQUENCES";
+					break;
 				default:
 					tag = "???";
 			}
@@ -2250,6 +2259,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/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index d100514..fc76858 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -104,6 +104,7 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
 	int			numForeignServers;
 	int			numDefaultACLs;
 	int			numEventTriggers;
+	int			numSeqAMs;
 
 	if (g_verbose)
 		write_msg(NULL, "reading schemas\n");
@@ -256,6 +257,10 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
 		write_msg(NULL, "reading policies\n");
 	getPolicies(fout, tblinfo, numTables);
 
+	if (g_verbose)
+		write_msg(NULL, "reading sequence access methods\n");
+	getSeqAMs(fout, &numSeqAMs);
+
 	*numTablesPtr = numTables;
 	return tblinfo;
 }
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index b0ee6e5..3968f04 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -179,6 +179,7 @@ static void dumpEventTrigger(Archive *fout, DumpOptions *dopt, EventTriggerInfo
 static void dumpTable(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpAttrDef(Archive *fout, DumpOptions *dopt, AttrDefInfo *adinfo);
+static void dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *tbinfo);
 static void dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo);
 static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo);
 static void dumpIndex(Archive *fout, DumpOptions *dopt, IndxInfo *indxinfo);
@@ -5996,6 +5997,71 @@ getRules(Archive *fout, int *numRules)
 }
 
 /*
+ * getSeqAMs:
+ *	  read all sequence access methods in the system catalogs and return them
+ *	  in the SeqAMInfo* structure
+ *
+ *	numSeqAMs is set to the number of access methods read in
+ */
+SeqAMInfo *
+getSeqAMs(Archive *fout, int *numSeqAMs)
+{
+	PGresult   *res;
+	int			ntups;
+	int			i;
+	PQExpBuffer query;
+	SeqAMInfo  *seqminfo;
+	int			i_tableoid,
+				i_oid,
+				i_seqamname;
+
+	/* Before 9.5, there were no sequence access methods */
+	if (fout->remoteVersion < 90500)
+	{
+		*numSeqAMs = 0;
+		return NULL;
+	}
+
+	query = createPQExpBuffer();
+
+	appendPQExpBuffer(query,
+					  "SELECT tableoid, oid, seqamname "
+					  "FROM pg_catalog.pg_seqam "
+					  "WHERE oid != '%u'::pg_catalog.oid",
+					  LOCAL_SEQAM_OID);
+
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+	ntups = PQntuples(res);
+	*numSeqAMs = ntups;
+
+	seqminfo = (SeqAMInfo *) pg_malloc(ntups * sizeof(SeqAMInfo));
+
+	i_tableoid = PQfnumber(res, "tableoid");
+	i_oid = PQfnumber(res, "oid");
+	i_seqamname = PQfnumber(res, "seqamname");
+
+	for (i = 0; i < ntups; i++)
+	{
+		seqminfo[i].dobj.objType = DO_SEQAM;
+		seqminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+		seqminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+		AssignDumpId(&seqminfo[i].dobj);
+		seqminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_seqamname));
+		seqminfo[i].dobj.namespace = NULL;
+
+		/* Decide whether we want to dump it */
+		selectDumpableObject(&(seqminfo[i].dobj));
+	}
+
+	PQclear(res);
+
+	destroyPQExpBuffer(query);
+
+	return seqminfo;
+}
+
+/*
  * getTriggers
  *	  get information about every trigger on a dumpable table
  *
@@ -8355,6 +8421,9 @@ dumpDumpableObject(Archive *fout, DumpOptions *dopt, DumpableObject *dobj)
 		case DO_POLICY:
 			dumpPolicy(fout, dopt, (PolicyInfo *) dobj);
 			break;
+		case DO_SEQAM:
+			dumpSeqAM(fout, dopt, (SeqAMInfo *) dobj);
+			break;
 		case DO_PRE_DATA_BOUNDARY:
 		case DO_POST_DATA_BOUNDARY:
 			/* never dumped, nothing to do */
@@ -14812,6 +14881,90 @@ findLastBuiltinOid_V70(Archive *fout)
 }
 
 /*
+ * dumpSeqAM
+ *	  write the declaration of one user-defined sequence access method
+ */
+static void
+dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *seqaminfo)
+{
+	PGresult   *res;
+	PQExpBuffer q;
+	PQExpBuffer delq;
+	PQExpBuffer labelq;
+	PQExpBuffer query;
+	char	   *seqamreloptions;
+	char	   *seqaminit;
+	char	   *seqamalloc;
+	char	   *seqamsetval;
+	char	   *seqamgetstate;
+	char	   *seqamsetstate;
+
+	/* Skip if not to be dumped */
+	if (!seqaminfo->dobj.dump || dopt->dataOnly)
+		return;
+
+	q = createPQExpBuffer();
+	delq = createPQExpBuffer();
+	labelq = createPQExpBuffer();
+	query = createPQExpBuffer();
+
+	appendPQExpBuffer(query, "SELECT seqamreloptions, seqaminit, "
+							 "seqamalloc, seqamsetval, "
+							 "seqamgetstate, seqamsetstate "
+							 "FROM pg_catalog.pg_seqam "
+							 "WHERE oid = '%u'::pg_catalog.oid",
+							 seqaminfo->dobj.catId.oid);
+
+	res = ExecuteSqlQueryForSingleRow(fout, query->data);
+
+	seqamreloptions = PQgetvalue(res, 0, PQfnumber(res, "seqamreloptions"));
+	seqaminit = PQgetvalue(res, 0, PQfnumber(res, "seqaminit"));
+	seqamalloc = PQgetvalue(res, 0, PQfnumber(res, "seqamalloc"));
+	seqamsetval = PQgetvalue(res, 0, PQfnumber(res, "seqamsetval"));
+	seqamgetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamgetstate"));
+	seqamsetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamsetstate"));
+
+	appendPQExpBuffer(q, "CREATE ACCESS METHOD FOR SEQUENCES %s AS (\n",
+					  fmtId(seqaminfo->dobj.name));
+
+	appendPQExpBuffer(q, "    RELOPTIONS = %s,\n", seqamreloptions);
+	appendPQExpBuffer(q, "    INIT = %s,\n", seqaminit);
+	appendPQExpBuffer(q, "    ALLOC = %s,\n", seqamalloc);
+	appendPQExpBuffer(q, "    SETVAL = %s,\n", seqamsetval);
+	appendPQExpBuffer(q, "    GETSTATE = %s,\n", seqamgetstate);
+	appendPQExpBuffer(q, "    SETSTATE = %s\n);\n", seqamsetstate);
+
+	appendPQExpBuffer(delq, "DROP ACCESS METHOD FOR SEQUENCES %s",
+					  fmtId(seqaminfo->dobj.name));
+
+	appendPQExpBuffer(labelq, "ACCESS METHOD FOR SEQUENCES %s",
+					  fmtId(seqaminfo->dobj.name));
+
+	if (dopt->binary_upgrade)
+		binary_upgrade_extension_member(q, &seqaminfo->dobj, labelq->data);
+
+	ArchiveEntry(fout, seqaminfo->dobj.catId, seqaminfo->dobj.dumpId,
+				 seqaminfo->dobj.name,
+				 NULL,
+				 NULL,
+				 "",
+				 false, "ACCESS METHOD FOR SEQUENCES", SECTION_PRE_DATA,
+				 q->data, delq->data, NULL,
+				 NULL, 0,
+				 NULL, NULL);
+
+	/* Dump Parser Comments */
+	dumpComment(fout, dopt, labelq->data,
+				NULL, "",
+				seqaminfo->dobj.catId, 0, seqaminfo->dobj.dumpId);
+
+	PQclear(res);
+	destroyPQExpBuffer(q);
+	destroyPQExpBuffer(delq);
+	destroyPQExpBuffer(labelq);
+}
+
+/*
  * dumpSequence
  *	  write the declaration (not data) of one user-defined sequence
  */
@@ -14913,8 +15066,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	{
 		PGresult   *res2;
 
-		printfPQExpBuffer(query, "SELECT a.seqamname\n"
-								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+		printfPQExpBuffer(query, "SELECT a.seqamname "
+								 "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c "
 								 "WHERE c.relam = a.oid AND c.oid = %u",
 						  tbinfo->dobj.catId.oid);
 
@@ -16019,6 +16172,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
 			case DO_FOREIGN_SERVER:
 			case DO_TRANSFORM:
 			case DO_BLOB:
+			case DO_SEQAM:
 				/* Pre-data objects: must come before the pre-data boundary */
 				addObjectDependency(preDataBound, dobj->dumpId);
 				break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 3d2b66a..141c684 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -77,7 +77,8 @@ typedef enum
 	DO_POST_DATA_BOUNDARY,
 	DO_EVENT_TRIGGER,
 	DO_REFRESH_MATVIEW,
-	DO_POLICY
+	DO_POLICY,
+	DO_SEQAM
 } DumpableObjectType;
 
 typedef struct _dumpableObject
@@ -305,6 +306,11 @@ typedef struct _ruleInfo
 	/* reloptions is only set if we need to dump the options with the rule */
 } RuleInfo;
 
+typedef struct _seqamInfo
+{
+	DumpableObject dobj;
+} SeqAMInfo;
+
 typedef struct _triggerInfo
 {
 	DumpableObject dobj;
@@ -561,5 +567,6 @@ extern void getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionIn
 					   int numExtensions);
 extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
 extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);
+extern SeqAMInfo *getSeqAMs(Archive *fout, int *numSeqAMs);
 
 #endif   /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index aa3e20a..ad81f0d 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -74,7 +74,8 @@ static const int oldObjectTypePriority[] =
 	13,							/* DO_POST_DATA_BOUNDARY */
 	20,							/* DO_EVENT_TRIGGER */
 	15,							/* DO_REFRESH_MATVIEW */
-	21							/* DO_POLICY */
+	21,							/* DO_POLICY */
+	5							/* DO_SEQAM */
 };
 
 /*
@@ -124,7 +125,8 @@ static const int newObjectTypePriority[] =
 	25,							/* DO_POST_DATA_BOUNDARY */
 	32,							/* DO_EVENT_TRIGGER */
 	33,							/* DO_REFRESH_MATVIEW */
-	34							/* DO_POLICY */
+	34,							/* DO_POLICY */
+	17							/* DO_SEQAM */
 };
 
 static DumpId preDataBoundId;
@@ -1469,6 +1471,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
 					 "POLICY (ID %d OID %u)",
 					 obj->dumpId, obj->catId.oid);
 			return;
+		case DO_SEQAM:
+			snprintf(buf, bufsize,
+					 "ACCESS METHOD FOR SEQUENCES %s  (ID %d OID %u)",
+					 obj->name, obj->dumpId, obj->catId.oid);
+			return;
 		case DO_PRE_DATA_BOUNDARY:
 			snprintf(buf, bufsize,
 					 "PRE-DATA BOUNDARY  (ID %d)",
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
index 7baa47c..b6e1474 100644
--- a/src/include/access/seqam.h
+++ b/src/include/access/seqam.h
@@ -26,6 +26,9 @@
 struct SequenceHandle;
 typedef struct SequenceHandle SequenceHandle;
 
+extern Oid DefineSeqAM(List *names, List *definition);
+extern void RemoveSeqAMById(Oid seqamoid);
+
 extern HeapTuple seqam_init(Relation seqrel, HeapTuple tuple,
 							int64 restart_value, bool restart_requested,
 							bool is_init);
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 5da18c2..42a2fa4 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -149,6 +149,7 @@ typedef enum ObjectClass
 	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
 	OCLASS_POLICY,				/* pg_policy */
 	OCLASS_TRANSFORM,			/* pg_transform */
+	OCLASS_SEQAM,				/* pg_seqam */
 	MAX_OCLASS					/* MUST BE LAST */
 } ObjectClass;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 10e2a1e..e4d81a7 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1298,6 +1298,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 faea991..0ec3cb3 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -237,6 +237,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

0003-gapless-sequence-v5.patchbinary/octet-stream; name=0003-gapless-sequence-v5.patchDownload
>From 09b53e0ea92e56c74d7bd69212ceff90e82fa7c7 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Tue, 9 Dec 2014 22:57:53 +0100
Subject: [PATCH 3/3] gapless sequence v5

---
 contrib/gapless_seq/Makefile                 |  63 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  78 ++++
 contrib/gapless_seq/gapless_seq.c            | 536 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/gapless_seq.sgml         |  22 ++
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 +++
 9 files changed, 971 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/gapless_seq.sgml
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql

diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..c2dd1be
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+   pg_sequence_get_state    
+----------------------------
+ {last_value,2,is_called,t}
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+   pg_sequence_get_state    
+----------------------------
+ {last_value,2,is_called,t}
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+ERROR:  cannot drop sequence access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on sequence access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..61f9c22
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,78 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_reloptions(INTERNAL, BOOLEAN)
+RETURNS BYTEA
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_init(INTERNAL, INTERNAL, BIGINT, BOOLEAN, BOOLEAN)
+RETURNS BYTEA
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_alloc(INTERNAL, INTERNAL, BIGINT, INTERNAL)
+RETURNS BIGINT
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_setval(INTERNAL, INTERNAL, BIGINT)
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_get_state(INTERNAL, INTERNAL)
+RETURNS TEXT[]
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_set_state(INTERNAL, INTERNAL, TEXT[])
+RETURNS VOID
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	is_called bool NOT NULL,
+	last_value bigint NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relam = (
+			SELECT oid FROM pg_seqam WHERE seqamname = 'gapless'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD FOR SEQUENCES gapless AS (
+	reloptions = seqam_gapless_reloptions,	/* reloptions parser is same as local (no reloptions) */
+	init = seqam_gapless_init,				/* init new gapless sequence */
+	alloc = seqam_gapless_alloc,			/* logs and returns each value... slow */
+	setval = seqam_gapless_setval,			/* setval support */
+	getstate = seqam_gapless_get_state,		/* pgdump support */
+	setstate = seqam_gapless_set_state		/* pgdump support */
+);
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..c7c21d5
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,536 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager = Gapless functions
+ *
+ *------------------------------------------------------------
+ */
+extern Datum seqam_gapless_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_init(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_gapless_set_state(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_reloptions);
+PG_FUNCTION_INFO_V1(seqam_gapless_init);
+PG_FUNCTION_INFO_V1(seqam_gapless_alloc);
+PG_FUNCTION_INFO_V1(seqam_gapless_setval);
+PG_FUNCTION_INFO_V1(seqam_gapless_get_state);
+PG_FUNCTION_INFO_V1(seqam_gapless_set_state);
+
+typedef struct GaplessAmdata
+{
+	uint32		xid;
+} GaplessAmdata;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	bool	is_called;
+	int64	last_value;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, int64 last_value,
+							   bool is_called, HeapTuple oldtuple);
+
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+Datum
+seqam_gapless_reloptions(PG_FUNCTION_ARGS)
+{
+	Datum		reloptions = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+Datum
+seqam_gapless_init(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	HeapTuple	tuple = (HeapTuple) PG_GETARG_POINTER(1);
+	int64		restart_value = (int64) PG_GETARG_INT64(2);
+	bool		restart_requested = (bool) PG_GETARG_BOOL(3);
+	bool		is_init = (bool) PG_GETARG_BOOL(4);
+	Oid			seqrelid = seqrel->rd_id;
+	TupleDesc	tupDesc = RelationGetDescr(seqrel);
+	Datum		amdata;
+	bool		isnull;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	TransactionId		local_xid = GetTopTransactionId();
+	GaplessAmdata	   *gapless_seq;
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, restart_value, false, valtuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+						 &isnull);
+
+	/* Make sure amdata is filled. */
+	if (isnull)
+	{
+		Datum			values[SEQ_COL_LASTCOL];
+		bool			nulls[SEQ_COL_LASTCOL];
+		bool			replace[SEQ_COL_LASTCOL];
+		struct varlena *vl = palloc0(VARHDRSZ + sizeof(GaplessAmdata));
+		HeapTuple		newtup;
+
+		memset(replace, false, sizeof(replace));
+		memset(nulls, false, sizeof(nulls));
+		memset(values, 0, sizeof(values));
+
+		replace[SEQ_COL_AMDATA - 1] = true;
+		nulls[SEQ_COL_AMDATA - 1] = false;
+
+		SET_VARSIZE(vl, VARHDRSZ + sizeof(GaplessAmdata));
+		values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+
+		newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replace);
+
+		/* Don't leak memory. */
+		heap_freetuple(tuple);
+		tuple = newtup;
+
+		amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+							 &isnull);
+	}
+
+	gapless_seq = (GaplessAmdata *)
+		VARDATA_ANY(DatumGetByteaP(amdata));
+
+	/* Update the xid info */
+	gapless_seq->xid = UInt32GetDatum(local_xid);
+
+	PG_RETURN_POINTER(tuple);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+Datum
+seqam_gapless_alloc(PG_FUNCTION_ARGS)
+{
+	Relation	seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	/* we ignore nreguested as gapless sequence can't do caching */
+	int64	   *last = (int64 *) PG_GETARG_POINTER(3);
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	FormData_pg_sequence   *seq;
+	GaplessAmdata	   *gapless_seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	gapless_seq = (GaplessAmdata *) VARDATA_ANY(&seq->amdata);
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/*
+	 * Insert or update the last value tuple.
+	 */
+	set_last_value_tup(valrel, seqrelid, result, true, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/*
+	 * If current tx is different fron the last one,
+	 * update the sequence tuple as well.
+	 *
+	 * We don't need to WAL log the update as the only thing we save to
+	 * sequence tuple is the active transaction id and we know that in case of
+	 * crash the transaction id will not be active so it's ok to lose the
+	 * update.
+	 */
+	if (gapless_seq->xid != local_xid)
+	{
+		sequence_start_update(seqh, true);
+		gapless_seq->xid = local_xid;
+		sequence_apply_update(seqh, true);
+		sequence_finish_update(seqh);
+	}
+
+	*last = result;
+	PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+Datum
+seqam_gapless_setval(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+Datum
+seqam_gapless_get_state(PG_FUNCTION_ARGS)
+{
+	Relation		seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+	Datum			datums[4];
+	Datum			val;
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	int64			last_value;
+	bool			is_called;
+
+	/*
+	 * Get the last value from the values table, if not found use start_value
+	 * from the sequence definition.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		last_value = v->last_value;
+		is_called = v->is_called;
+	}
+	else
+	{
+		FormData_pg_sequence *seq = (FormData_pg_sequence *)
+			GETSTRUCT(sequence_read_tuple(seqh));
+		last_value = seq->start_value;
+		is_called = false;
+		sequence_release_tuple(seqh);
+	}
+
+	datums[0] = CStringGetTextDatum("last_value");
+	val = DirectFunctionCall1(int8out, Int64GetDatum(last_value));
+	datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+	datums[2] = CStringGetTextDatum("is_called");
+	val = DirectFunctionCall1(boolout, BoolGetDatum(is_called));
+	datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+	PG_RETURN_ARRAYTYPE_P(construct_array(datums, 4, TEXTOID, -1, false, 'i'));
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+Datum
+seqam_gapless_set_state(PG_FUNCTION_ARGS)
+{
+	Relation		seqrel = (Relation) PG_GETARG_POINTER(0);
+	SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+	ArrayType	   *array = PG_GETARG_ARRAYTYPE_P(2);
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	int64			last_value = 0;
+	bool			is_called = false;
+	bool			last_value_found = false,
+					is_called_found = false;
+	Datum		   *datums;
+	int				count;
+	int				i;
+	FormData_pg_sequence   *seq;
+	GaplessAmdata	   *gapless_seq;
+	TransactionId local_xid = GetTopTransactionId();
+
+	Assert(ARR_ELEMTYPE(array) == TEXTOID && ARR_NDIM(array) == 1 &&
+		   (ARR_DIMS(array)[0]) % 2 == 0 && !ARR_HASNULL(array));
+
+	deconstruct_array(array,
+					  TEXTOID, -1, false, 'i',
+					  &datums, NULL, &count);
+
+	count /= 2;
+	for (i = 0; i < count; ++i)
+	{
+		char   *key,
+			   *val;
+
+		key = TextDatumGetCString(datums[i * 2]);
+		val = TextDatumGetCString(datums[i * 2 + 1]);
+
+		if (pg_strcasecmp(key, "last_value") == 0)
+		{
+			last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+														CStringGetDatum(val)));
+			last_value_found = true;
+		}
+		else if (pg_strcasecmp(key, "is_called") == 0)
+		{
+			is_called = DatumGetBool(DirectFunctionCall1(boolin,
+														CStringGetDatum(val)));
+			is_called_found = true;
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid state key \"%s\" for gapless sequence",
+							key)));
+	}
+
+	if (!last_value_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("last_value is required parameter for gapless sequence")));
+
+	if (!is_called_found)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("is_called is required parameter for gapless sequence")));
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	gapless_seq = (GaplessAmdata *) VARDATA_ANY(&seq->amdata);
+
+	sequence_check_range(last_value, seq->min_value, seq->max_value, "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, last_value, is_called, tuple);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	gapless_seq->xid = local_xid;
+	sequence_apply_update(seqh, true);
+	sequence_finish_update(seqh);
+	sequence_release_tuple(seqh);
+
+	PG_FREE_IF_COPY(array, 2);
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormData_pg_sequence   *seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	GaplessAmdata	   *gapless_seq = (GaplessAmdata *) VARDATA_ANY(&seq->amdata);
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (gapless_seq->xid != local_xid &&
+		   TransactionIdIsInProgress(gapless_seq->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(gapless_seq->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, int64 last_value, bool is_called, HeapTuple oldtuple)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = BoolGetDatum(is_called);
+		values[2] = Int64GetDatum(last_value);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/gapless_seq.sgml b/contrib/gapless_seq/gapless_seq.sgml
new file mode 100644
index 0000000..3eb536f
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.sgml
@@ -0,0 +1,22 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless_seq" xreflabel="gapless_seq">
+ <title>gapless sequence</title>
+
+ <indexterm zone="gapless_seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence.
+  This could be very costly and is not recommended for general
+  usage except in specific applications that require this feature.
+ </para>
+
+</sect1>
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..1913947
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD FOR SEQUENCES gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
-- 
1.9.1

#83Vik Fearing
vik@2ndquadrant.fr
In reply to: Petr Jelinek (#82)
Re: Sequence Access Method WIP

I've been looking at these patches a bit and here are some comments:

Patch 1: seqam

I would like to see an example in the docs for CREATE SEQUENCE. That's
perhaps not possible (or desirable) with only the "local" seqam? Not sure.

In the docs for pg_class, there is no mention that relam refers to
pg_seqam for sequences but pg_am for all other types.

There are errant half-sentences in the documentation, for example "to
the options in the CREATE SEQUENCE or ALTER SEQUENCE statement." in
Sequence Access Method Functions.

I'd prefer a README instead of the long comment at the start of seqam.c.
The other ams do that.

As mentioned upthread, this patch isn't a seamless replacement for
what's already there because of the amdata field. I wasn't part of the
conversation of FOSDEM unfortunately, and there's not enough information
in this thread to know why this solution is preferred over each seqam
having its own table type with all the columns it needs. I see that
Heikki is waffling a bit between the two, and I have a fairly strong
opinion that amdata should be split into separate columns. The patch
already destroys and recreates what it needs when changing access method
via ALTER SEQUENCE, so I don't really see what the problem is.

There is no psql tab completion for the new USING clause in ALTER
SEQUENCE, and in looking at that I noticed we don't have tab completion
for CREATE SEQUENCE at all. I know we don't complete everything, but if
we're going to do ALTER, I think we should do CREATE. I'll submit a
patch for that on its own thread, but then this patch will need to
modify it to include USING.

There is no \d command for sequence access methods. Without querying
pg_seqam directly, how does one discover what's available?

There are some unfinished comments, such as "When relam is specified,
record dependency on the" in heap.c.

Comments and code in pg_dump.c check that the server is at least 9.5.
Those'll need to be changed to at least 9.6.

Patch 2: seqam ddl

When defining a new access method for sequences, it is possible to list
the arguments multiple times (last one wins). Other defel loops raise
an error if the argument is specified more than once. I haven't looked
at all of such loops to see if this is the only odd man out or not, but
I prefer the error behavior.

It doesn't seem possible to create a comment for a seqam, is that
intentional? Ah, after more review I see it is possible, just not
documented. I think that needs to be corrected.

Same comment as above about testing for 9.5.

Patch 3: gapless_seq

I really like the idea of having a gapless sequence in contrib.
Although it has big potential to be abused, doing it manually when it's
needed (like for invoices, at least in France) is a major pain. So big
+1 for including this.

However, the patch doesn't update the main contrib Makefile so you have
to compile it explicitly. It also doesn't have the .sgml file in the
right place, so that's not installed either.

There is a FIXME in get_last_value_tup() which should probably be
removed in light of the code comment in catcache.c stating that pg_seqam
is too small to benefit from an index scan.

It would be nice to be able to specify an access method when declaring a
serial or bigserial column. This could be a separate patch later on,
though.

On the whole, I think this is a pretty good patchset. Aside from the
design decision of whether amdata is a single opaque column or a set of
columns, there are only a few things that need to be changed before it's
ready for committer, and those things are mostly documentation.
--
Vik Fearing +33 6 46 75 15 36
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#84Petr Jelinek
petr@2ndquadrant.com
In reply to: Vik Fearing (#83)
Re: Sequence Access Method WIP

On 2015-06-15 11:32, Vik Fearing wrote:

I've been looking at these patches a bit and here are some comments:

Thanks for looking at this.

Patch 1: seqam

I would like to see an example in the docs for CREATE SEQUENCE. That's
perhaps not possible (or desirable) with only the "local" seqam? Not sure.

It is possible to have example with local seqam, it might be confusing
though, given it produces same results as not putting USING in the query.

In the docs for pg_class, there is no mention that relam refers to
pg_seqam for sequences but pg_am for all other types.

There are errant half-sentences in the documentation, for example "to
the options in the CREATE SEQUENCE or ALTER SEQUENCE statement." in
Sequence Access Method Functions.

I think that's the side effect of all the rebases and rewrites over the
2y(!) that this has been going forward and back. It can be easily fixed
by proof reading before final submission. I didn't pay too much
attention yet because it's not clear how the docs should look like if
there is no real agreement on the api. (This applies to other comments
about docs as well)

I'd prefer a README instead of the long comment at the start of seqam.c.
The other ams do that.

OK, since things have been moved to separate directory, README is
doable, I personally prefer the docs in the main .c file usually but I
know project uses README sometimes for this.

As mentioned upthread, this patch isn't a seamless replacement for
what's already there because of the amdata field. I wasn't part of the
conversation of FOSDEM unfortunately, and there's not enough information
in this thread to know why this solution is preferred over each seqam
having its own table type with all the columns it needs. I see that
Heikki is waffling a bit between the two, and I have a fairly strong
opinion that amdata should be split into separate columns. The patch
already destroys and recreates what it needs when changing access method
via ALTER SEQUENCE, so I don't really see what the problem is.

FOSDEM was just about agreeing that amdata is simpler after we discussed
it with Heikki. Nothing too important you missed there I guess.

I can try to summarize what are the differences:
- amdata is somewhat simpler in terms of code for both init, alter and
DDL, since with custom columns you have to specify them somehow and deal
with them in catalog, also ALTER SEQUENCE USING means that there are
going to be colums marked as deleted which produces needless waste, etc
- amdata make it easier to change the storage model as the tuple
descriptor is same for all sequences
- the separate columns are much nicer from user point of view
- my opinion is that separate columns also more nicely separate state
from options and I think that if we move to separate storage model,
there can be state table per AM which solves the tuple descriptor issue
- there is probably some slight performance benefit to amdata but I
don't think it's big enough to be important

I personally have slight preference to separate columns design, but I am
ok with both ways honestly.

There is no \d command for sequence access methods. Without querying
pg_seqam directly, how does one discover what's available?

Good point.

Patch 2: seqam ddl

When defining a new access method for sequences, it is possible to list
the arguments multiple times (last one wins). Other defel loops raise
an error if the argument is specified more than once. I haven't looked
at all of such loops to see if this is the only odd man out or not, but
I prefer the error behavior.

Hmm yeah, there should be error. I think only tsearch doesn't enforce
errors from the existing stuff, should probably be fixed as well
(separately of course).

Patch 3: gapless_seq

I really like the idea of having a gapless sequence in contrib.
Although it has big potential to be abused, doing it manually when it's
needed (like for invoices, at least in France) is a major pain. So big
+1 for including this.

Yeah, people make gapless sequences regardless, it's better to provide
them one that behaves correctly, also it's quite good test for the API.

It would be nice to be able to specify an access method when declaring a
serial or bigserial column. This could be a separate patch later on,
though.

The patch originally had GUC for this, but Heikki didn't like it so it's
left for the future developments.

On the whole, I think this is a pretty good patchset. Aside from the
design decision of whether amdata is a single opaque column or a set of
columns, there are only a few things that need to be changed before it's
ready for committer, and those things are mostly documentation.

Unfortunately the amdata being opaque vs set of columns is the main
issue here.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#85Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Petr Jelinek (#84)
Re: Sequence Access Method WIP

So, we have this patch in the commitfest again. Let's see where we are,
and try to find a consensus on what needs to be done before this can be
committed.

On 06/17/2015 06:51 PM, Petr Jelinek wrote:

On 2015-06-15 11:32, Vik Fearing wrote:

I've been looking at these patches a bit and here are some comments:

Thanks for looking at this.

+1, thanks Vik.

As mentioned upthread, this patch isn't a seamless replacement for
what's already there because of the amdata field. I wasn't part of the
conversation of FOSDEM unfortunately, and there's not enough information
in this thread to know why this solution is preferred over each seqam
having its own table type with all the columns it needs. I see that
Heikki is waffling a bit between the two, and I have a fairly strong
opinion that amdata should be split into separate columns. The patch
already destroys and recreates what it needs when changing access method
via ALTER SEQUENCE, so I don't really see what the problem is.

FOSDEM was just about agreeing that amdata is simpler after we discussed
it with Heikki. Nothing too important you missed there I guess.

I can try to summarize what are the differences:
- amdata is somewhat simpler in terms of code for both init, alter and
DDL, since with custom columns you have to specify them somehow and deal
with them in catalog, also ALTER SEQUENCE USING means that there are
going to be colums marked as deleted which produces needless waste, etc
- amdata make it easier to change the storage model as the tuple
descriptor is same for all sequences
- the separate columns are much nicer from user point of view
- my opinion is that separate columns also more nicely separate state
from options and I think that if we move to separate storage model,
there can be state table per AM which solves the tuple descriptor issue
- there is probably some slight performance benefit to amdata but I
don't think it's big enough to be important

I personally have slight preference to separate columns design, but I am
ok with both ways honestly.

Regarding the amdata issue, I'm also leaning towards set of columns.
I've felt that way all along, but not very strongly, so I relented at
some point when Andres felt strongly that a single column would be
better. But the more I think about it, the more I feel that separate
columns really would be better. As evidence, I offer this recent thread:

Tom said
(/messages/by-id/8739.1436893588@sss.pgh.pa.us):

I really don't see what's wrong with "SELECT last_value FROM sequence",
especially since that has worked in every Postgres version since 6.x.
Anyone slightly worried about backwards compatibility wouldn't use
an equivalent function even if we did add one.

If we went with the single amdata column, that would break. Or we'd need
to leave last_value as a separate column anyway, and leave it unused for
sequence AMs where it's not applicable. But that's a bit ugly too.

Jim Nasby said in the same thread:

FWIW, I think it'd be better to have a pg_sequences view that's the
equivalent of SELECT * FROM <sequence> for every sequence in the
database. That would let you get whatever info you needed.

Creating such a view would be difficult if all the sequences have a
different set of columns. But when you think about it, it's not really
any better with a single amdata column. You can't easily access the data
in the amdata column that way either.

Anyway, that's my opinion. Several others have weighed in to support
separate columns, too, so I think that is the consensus. Separate
columns it is.

There is no \d command for sequence access methods. Without querying
pg_seqam directly, how does one discover what's available?

Good point.

Well, you can query pg_seqam. I don't think this deserves a \d command.

On the whole, I think this is a pretty good patchset. Aside from the
design decision of whether amdata is a single opaque column or a set of
columns, there are only a few things that need to be changed before it's
ready for committer, and those things are mostly documentation.

Unfortunately the amdata being opaque vs set of columns is the main
issue here.

There was discussion on another thread on how the current sequence AM
API is modeled after the indexam API, at
/messages/by-id/3896.1437059303@sss.pgh.pa.us. Will
need to do something about that too.

Petr, is this enough feedback on this patch for this commitfest, or are
there some other issues you want to discuss before I mark this as returned?

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#86Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#85)
Re: Sequence Access Method WIP

On 2015-07-28 20:11, Heikki Linnakangas wrote:

Petr, is this enough feedback on this patch for this commitfest, or are
there some other issues you want to discuss before I mark this as returned?

You can mark it as returned, I didn't have much time to actually do much
useful work on this in the current CF.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#87Thom Brown
thom@linux.com
In reply to: Petr Jelinek (#86)
Re: Sequence Access Method WIP

On 28 July 2015 at 19:51, Petr Jelinek <petr@2ndquadrant.com> wrote:

On 2015-07-28 20:11, Heikki Linnakangas wrote:

Petr, is this enough feedback on this patch for this commitfest, or are
there some other issues you want to discuss before I mark this as
returned?

You can mark it as returned, I didn't have much time to actually do much
useful work on this in the current CF.

Is this now dependant on the work Alexander Korotkov is doing on the AM
interface?

Thom

#88Petr Jelinek
petr@2ndquadrant.com
In reply to: Thom Brown (#87)
Re: Sequence Access Method WIP

On 2015-09-16 13:21, Thom Brown wrote:

On 28 July 2015 at 19:51, Petr Jelinek <petr@2ndquadrant.com
<mailto:petr@2ndquadrant.com>> wrote:

On 2015-07-28 20:11, Heikki Linnakangas wrote:

Petr, is this enough feedback on this patch for this commitfest,
or are
there some other issues you want to discuss before I mark this
as returned?

You can mark it as returned, I didn't have much time to actually do
much useful work on this in the current CF.

Is this now dependant on the work Alexander Korotkov is doing on the AM
interface?

Yes.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#89Petr Jelinek
petr@2ndquadrant.com
In reply to: Heikki Linnakangas (#85)
1 attachment(s)
Re: Sequence Access Method WIP

Hi,

here is another try at this. I had some more time to think about various
corner cases (like the sequence not having TOAST).

I decided for this version to try bit different approach and that is to
use custom datatype for the sequence access method state. So there is
one extra amstate column and the access method defines the custom type.

The idea is that the custom type should be fixed width so that we can do
proper checks during the creation of access method for size and stuff.
It also makes the interface bit cleaner as we can provide method for
reading and saving of the state and abstract the internals more nicely.
It also solves the issues with critical sections vs memory allocations
when the access methods wants to save completely new state.

It also means that get_state and set_state interfaces just work with
text representation of custom type, no need to have complex conversions
to arrays and back.

One downside is that this means access methods can't use composite types
as those are not fixed width (nor plain) so the names of the individual
items in the output are somewhat hidden (see the modified create_view
test for example of how this affects things).

Other than that, this is based on the new am api by Alexander Korotkov
[1]: https://commitfest.postgresql.org/8/336/
which can have either value "i" for index or "S" for sequence (same as
relkind in pg_class for those).

I didn't attach DDL and the gapless sequence yet, mainly because I don't
want to waste time rewriting it again in case we can't agree that this
approach is good (based on the long discussion and resulting several
rewrites wrt the multiple columns vs bytea previously).

[1]: https://commitfest.postgresql.org/8/336/

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

seqam-2015-12-31.patchbinary/octet-stream; name=seqam-2015-12-31.patchDownload
>From 1a5ddbfe0b51eef2c364fe30e92d962107e87c0c Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Thu, 31 Dec 2015 11:05:52 +0200
Subject: seqam 2015-12-31

---
 doc/src/sgml/ref/alter_sequence.sgml          |   55 +
 doc/src/sgml/ref/create_sequence.sgml         |   25 +
 src/backend/access/Makefile                   |    4 +-
 src/backend/access/common/reloptions.c        |   10 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqam.c           |  185 +++
 src/backend/access/sequence/seqlocal.c        |  398 +++++++
 src/backend/bootstrap/bootparse.y             |    1 +
 src/backend/catalog/heap.c                    |   16 +-
 src/backend/catalog/objectaddress.c           |    3 +-
 src/backend/catalog/toasting.c                |    1 +
 src/backend/commands/cluster.c                |    1 +
 src/backend/commands/createas.c               |    3 +-
 src/backend/commands/indexcmds.c              |   12 +-
 src/backend/commands/opclasscmds.c            |   15 +-
 src/backend/commands/sequence.c               | 1501 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |   46 +-
 src/backend/commands/typecmds.c               |    3 +-
 src/backend/commands/view.c                   |    3 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   77 +-
 src/backend/parser/parse_utilcmd.c            |    6 +-
 src/backend/tcop/utility.c                    |    6 +-
 src/backend/utils/cache/catcache.c            |    4 +-
 src/backend/utils/cache/relcache.c            |   53 +-
 src/backend/utils/cache/syscache.c            |    8 +-
 src/backend/utils/misc/guc.c                  |    1 +
 src/bin/pg_dump/pg_dump.c                     |  115 +-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/psql/describe.c                       |  112 +-
 src/include/access/reloptions.h               |    2 +-
 src/include/access/seqam.h                    |  109 ++
 src/include/catalog/heap.h                    |    1 +
 src/include/catalog/indexing.h                |    4 +-
 src/include/catalog/pg_am.h                   |   25 +-
 src/include/catalog/pg_proc.h                 |   13 +
 src/include/catalog/pg_type.h                 |    5 +
 src/include/commands/defrem.h                 |    2 +-
 src/include/commands/sequence.h               |   31 +-
 src/include/commands/tablecmds.h              |    2 +-
 src/include/nodes/nodes.h                     |    3 +-
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/utils/rel.h                       |    5 +-
 src/include/utils/syscache.h                  |    2 +-
 src/test/regress/expected/create_view.out     |    5 +-
 src/test/regress/expected/sequence.out        |   35 +-
 src/test/regress/expected/updatable_views.out |   95 +-
 src/test/regress/sql/create_view.sql          |    5 +-
 src/test/regress/sql/sequence.sql             |   10 +-
 50 files changed, 2278 insertions(+), 774 deletions(-)
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqam.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqam.h

diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a..223403d 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,7 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  tablesample transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc sequence \
+			  spgist tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index b52846f..d995b85 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -888,7 +888,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, amoptions_function amoptions)
@@ -919,7 +920,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, amoptions_function amoptio
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1372,14 +1374,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
  *
  *	amoptions	Oid of option parser
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+am_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 {
 	Assert(amoptions);
 
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..36c1bd8
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,185 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amstate.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_am.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache2(AMNAMEKIND, PointerGetDatum(amname),
+							CharGetDatum(AMKIND_SEQUENCE));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
+
+/*
+ * GetSequenceAM - call the specified access method handler routine
+ * to get its SequenceAM struct.
+ */
+SequenceAM *
+GetSequenceAM(Oid amhandler)
+{
+	Datum			datum;
+	SequenceAM	   *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SequenceAM *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SequenceAM))
+		elog(ERROR, "sequence access method handler function %u did not return an SequenceAM struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * GetSequenceAMByAMId - look up the handler of the access method
+ * for the given OID, and retrieve its SequenceAM struct.
+ */
+SequenceAM *
+GetSequenceAMByAMId(Oid amoid)
+{
+	HeapTuple		tuple;
+	regproc			amhandler;
+	Form_pg_am		amform;
+
+	/* 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);
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("sequence access method \"%s\" does not have a handler",
+						NameStr(amform->amname))));
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function. */
+	return GetSequenceAM(amhandler);
+}
+
+
+SequenceAM *
+GetSequenceAMForRelation(Relation relation)
+{
+	SequenceAM *seqam;
+	SequenceAM *cseqam;
+
+	if (relation->rd_seqam == NULL)
+	{
+		if (!OidIsValid(relation->rd_rel->relam))
+		{
+			char *relname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(relation)),
+													   RelationGetRelationName(relation));
+			elog(ERROR, "relation %s isn't sequence", relname);
+		}
+
+		seqam = GetSequenceAMByAMId(relation->rd_rel->relam);
+		/* Save the data for later reuse in CacheMemoryContext */
+		cseqam = (SequenceAM *) MemoryContextAlloc(CacheMemoryContext,
+												   sizeof(SequenceAM));
+		memcpy(cseqam, seqam, sizeof(SequenceAM));
+		relation->rd_seqam = cseqam;
+	}
+
+	return relation->rd_seqam;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..22f1740
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,398 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+#include "utils/typcache.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+static bytea *seqam_local_reloptions(Datum reloptions, bool validate);
+static Datum seqam_local_init(Relation seqrel, Form_pg_sequence seq,
+							  int64 restart_value, bool restart_requested,
+							  bool is_init);
+static int64 seqam_local_alloc(Relation seqrel, SequenceHandle *seqh,
+							   int64 nrequested, int64 *last);
+static void seqam_local_setval(Relation seqrel, SequenceHandle *seqh,
+							   int64 new_value);
+static Datum seqam_local_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+								  Datum amstate);
+
+static char *
+seqam_local_state_get_part(StringInfo buf, char *ptr, bool *found)
+{
+	*found = false;
+
+	resetStringInfo(buf);
+
+	while (*ptr != ',' && *ptr != ')')
+	{
+		char		ch = *ptr++;
+
+		if (ch == '\0')
+		{
+			*found = false;
+			return ptr;
+		}
+
+		appendStringInfoChar(buf, ch);
+
+		if (!*found)
+			*found = true;
+	}
+
+	return ptr;
+}
+
+Datum
+seqam_local_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr;
+	Datum		d;
+	bool		found;
+	StringInfoData		buf;
+	LocalSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	initStringInfo(&buf);
+
+	/* last_value is required */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (!found)
+		goto malformed;
+	d = DirectFunctionCall1(int8in, CStringGetDatum(buf.data));
+	state->last_value = DatumGetInt64(d);
+	if (*ptr == ',')
+		ptr++;
+
+	/* is_called is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(boolin, CStringGetDatum(buf.data));
+		state->is_called = DatumGetBool(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->is_called = true;
+
+	/* log_cnt is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(int4in, CStringGetDatum(buf.data));
+		state->log_cnt = DatumGetInt32(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->log_cnt = 0;
+
+	if (*ptr != ')' || *(++ptr) != '\0')
+		goto malformed;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_local_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_local_state_out(PG_FUNCTION_ARGS)
+{
+	LocalSequenceState *state = (LocalSequenceState *) PG_GETARG_POINTER(0);
+	StringInfoData		buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s,%d)",
+					 state->last_value, state->is_called ? "t" : "f",
+					 state->log_cnt);
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+Datum
+seqam_local_handler(PG_FUNCTION_ARGS)
+{
+	SequenceAM *seqam = makeNode(SequenceAM);
+
+	seqam->StateTypeOid = SEQAMLOCALSTATEOID;
+	seqam->ParseRelOption = seqam_local_reloptions;
+	seqam->Init = seqam_local_init;
+	seqam->Alloc = seqam_local_alloc;
+	seqam->Setval = seqam_local_setval;
+	seqam->GetState = seqam_local_get_state;
+	seqam->SetState = seqam_local_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+static bytea *
+seqam_local_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+static Datum
+seqam_local_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	LocalSequenceState	   *localseq;
+
+	if (is_init)
+		localseq = palloc0(sizeof(LocalSequenceState));
+	else
+		localseq = (LocalSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		localseq->last_value = restart_value;
+		localseq->is_called = false;
+	}
+
+	localseq->log_cnt = 0;
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+static int64
+seqam_local_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+				  int64 *last)
+{
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) seq->amstate;
+
+	next = result = localseq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = localseq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_start_update(seqh, logit);
+
+	if (logit)
+	{
+		localseq->last_value = next;
+		localseq->log_cnt = 0;
+		localseq->is_called = true;
+
+		sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	}
+
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_save_state(seqh, PointerGetDatum(localseq), false);
+
+	sequence_finish_update(seqh);
+
+	return result;
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+static void
+seqam_local_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq;
+
+	amstate = sequence_read_state(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	localseq->last_value = new_value;		/* last fetched number */
+	localseq->is_called = true;
+	localseq->log_cnt = 0;			/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+static Datum
+seqam_local_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq = palloc(sizeof(LocalSequenceState));
+
+	amstate = sequence_read_state(seqh);
+	memcpy(localseq, DatumGetPointer(amstate), sizeof(LocalSequenceState));
+	sequence_release_tuple(seqh);
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+static void
+seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+					  Datum amstate)
+{
+	(void) sequence_read_state(seqh);
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index d8d1b06..0eb92bd 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -251,6 +251,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 04c4f8f..d5578d9 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -87,6 +87,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -848,6 +849,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -921,6 +923,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1034,6 +1037,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1262,6 +1266,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1274,7 +1279,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1329,6 +1335,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = AccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index e44d7d0..c9ff22b 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
@@ -1487,7 +1488,7 @@ get_object_address_opcf(ObjectType objtype, List *objname, bool missing_ok)
 	ObjectAddress address;
 
 	/* XXX no missing_ok support here */
-	amoid = get_am_oid(strVal(linitial(objname)), false);
+	amoid = get_index_am_oid(strVal(linitial(objname)), false);
 	objname = list_copy_tail(objname, 1);
 
 	switch (objtype)
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 3652d7b..aff276b 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -290,6 +290,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 7a7fd95..241ce21 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -679,6 +679,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 41183f6..ba2ff14 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b794d59..f92475f 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -154,7 +154,8 @@ CheckIndexCompatible(Oid oldId,
 	Assert(numberOfAttributes <= INDEX_MAX_KEYS);
 
 	/* look up the access method */
-	tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
+	tuple = SearchSysCache2(AMNAMEKIND, PointerGetDatum(accessMethodName),
+							CharGetDatum(AMKIND_INDEX));
 	if (!HeapTupleIsValid(tuple))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -470,7 +471,8 @@ DefineIndex(Oid relationId,
 	 * look up the access method, verify it can handle the requested features
 	 */
 	accessMethodName = stmt->accessMethod;
-	tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
+	tuple = SearchSysCache2(AMNAMEKIND, PointerGetDatum(accessMethodName),
+							CharGetDatum(AMKIND_INDEX));
 	if (!HeapTupleIsValid(tuple))
 	{
 		/*
@@ -482,7 +484,9 @@ DefineIndex(Oid relationId,
 			ereport(NOTICE,
 					(errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
 			accessMethodName = "gist";
-			tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
+			tuple = SearchSysCache2(AMNAMEKIND,
+									PointerGetDatum(accessMethodName),
+									CharGetDatum(AMKIND_INDEX));
 		}
 
 		if (!HeapTupleIsValid(tuple))
@@ -533,7 +537,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 7a50b6a..14f8e2d 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -353,7 +353,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
 					   get_namespace_name(namespaceoid));
 
 	/* Get necessary info about access method */
-	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
+	tup = SearchSysCache2(AMNAMEKIND, CStringGetDatum(stmt->amname),
+						  CharGetDatum(AMKIND_INDEX));
 	if (!HeapTupleIsValid(tup))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -774,7 +775,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
 					   get_namespace_name(namespaceoid));
 
 	/* Get access method OID, throwing an error if it doesn't exist. */
-	amoid = get_am_oid(stmt->amname, false);
+	amoid = get_index_am_oid(stmt->amname, false);
 
 	/* XXX Should we make any privilege check against the AM? */
 
@@ -811,7 +812,8 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
 	IndexAmRoutine *amroutine;
 
 	/* Get necessary info about access method */
-	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
+	tup = SearchSysCache2(AMNAMEKIND, CStringGetDatum(stmt->amname),
+						  CharGetDatum(AMKIND_INDEX));
 	if (!HeapTupleIsValid(tup))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1758,17 +1760,18 @@ IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
 }
 
 /*
- * get_am_oid - given an access method name, look up the OID
+ * get_index_am_oid - given an index 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)
+get_index_am_oid(const char *amname, bool missing_ok)
 {
 	Oid			oid;
 
-	oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
+	oid = GetSysCacheOid2(AMNAMEKIND, CStringGetDatum(amname),
+						  CharGetDatum(AMKIND_INDEX));
 	if (!OidIsValid(oid) && !missing_ok)
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 9c1037f..5d92918 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,14 +14,19 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/tupmacs.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_type.h"
@@ -36,19 +41,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,24 +81,112 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	Oid			statetyp;
+	int16		statetyplen;
+	bool		statetypbyval;
+	HeapTupleData tup;
+	bool		inupdate;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
  */
 static SeqTableData *last_used_seq = NULL;
 
+static HeapTuple build_seq_tuple(Relation rel, SequenceAM *seqam,
+								 Form_pg_sequence new, int64 restart_value);
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+
+/*
+ * Build template column definition for a sequence relation.
+*/
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_not_null = true;
+	coldef->is_from_type = false;
+	/* Force plain storage. */
+	coldef->storage = 'p';
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(Oid amstateTypeOid)
+{
+	List	   *seqcols;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		switch (colid)
+		{
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+			case SEQ_COL_AMSTATE:
+				coldef->typeName = makeTypeNameFromOid(amstateTypeOid, -1);
+				coldef->colname = "amstate";
+				break;
+		}
+
+		seqcols = lappend(seqcols, coldef);
+	}
 
+	return seqcols;
+}
 
 /*
  * DefineSequence
@@ -111,14 +199,12 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
+	List	   *seqcols;
+	SequenceAM *seqam;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -147,102 +233,33 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
-	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
-
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
-		{
-			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
-				break;
-			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
-				break;
-			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
-				break;
-			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
-				break;
-			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
-				break;
-			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
-			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
-				break;
-		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
-	}
+	if (seq->accessMethod)
+		seqamid = get_seqam_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
+	seqam = GetSequenceAMByAMId(seqamid);
+	/* Build column definitions. */
+	seqcols = BuildSeqColumnDefList(seqam->StateTypeOid);
 
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
+	stmt->tableElts = seqcols;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	/* Build new sequence tuple and store it. */
+	tuple = build_seq_tuple(rel, seqam, &new, new.start_value);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +271,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +285,89 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
+	TupleDesc			tupDesc;
+	Datum	   values[SEQ_COL_LASTCOL];
+	bool	   nulls[SEQ_COL_LASTCOL];
+	SequenceAM *seqam;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
+	seqam = GetSequenceAMForRelation(seqrel);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
-	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tupDesc = RelationGetDescr(seqrel);
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(seqrel, seq, seq->start_value,
+											  true, false);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
+}
+
+/*
+ * Build sequence tuple based on the sequence form and fill in the
+ * sequence AM specific info as well.
+ */
+static HeapTuple
+build_seq_tuple(Relation rel, SequenceAM *seqam, Form_pg_sequence new,
+				int64 restart_value)
+{
+	TupleDesc	tupDesc;
+	HeapTuple	tuple;
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+
+	tupDesc = RelationGetDescr(rel);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new->start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new->increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new->max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new->min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new->cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new->is_cycled);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(rel, new, restart_value,
+											  false, true);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	return tuple;
 }
 
 /*
@@ -361,7 +410,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +430,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,6 +438,69 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 }
 
 /*
+ * Replace the type of amstate column.
+ *
+ * We don't do AlterTable here as that produces dead columns which we don't
+ * want. This is safe because the sequence page is controlled by code in this
+ * module and isn't changed the same way as a table.
+ *
+ * TODO: check if anybody is depending on the type associated with the
+ * sequence.
+ */
+static void
+replace_sequence_amstate_col(Oid seqrelid, Oid typid)
+{
+	Relation	attr_rel;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	HeapTuple	tp,
+				attr_tuple,
+				newattr_tuple;
+	Form_pg_type	typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+
+	memset(nulls, 0, sizeof(nulls));
+	memset(replace, 0, sizeof(replace));
+
+	replace[Anum_pg_attribute_atttypid - 1] = true;
+	replace[Anum_pg_attribute_attlen - 1] = true;
+	replace[Anum_pg_attribute_attbyval - 1] = true;
+	replace[Anum_pg_attribute_attalign - 1] = true;
+
+	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(typid);
+	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(typtup->typlen);
+	values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(typtup->typbyval);
+	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(typtup->typalign);
+
+	/* Build DROP command for amstate of old AM. */
+	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+	attr_tuple = SearchSysCache2(ATTNUM,
+								 ObjectIdGetDatum(seqrelid),
+								 Int16GetDatum(SEQ_COL_AMSTATE));
+	if (!HeapTupleIsValid(attr_tuple))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 SEQ_COL_AMSTATE, seqrelid);
+
+	newattr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel),
+									  values, nulls, replace);
+	simple_heap_update(attr_rel, &newattr_tuple->t_self, newattr_tuple);
+	CatalogUpdateIndexes(attr_rel, newattr_tuple);
+
+	ReleaseSysCache(tp);
+	heap_freetuple(newattr_tuple);
+	ReleaseSysCache(attr_tuple);
+
+	heap_close(attr_rel, RowExclusiveLock);
+}
+
+/*
  * AlterSequence
  *
  * Modify the definition of a sequence relation
@@ -406,19 +508,24 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence seq,
+					 new;
 	List	   *owned_by;
 	ObjectAddress address;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
+	SequenceAM *oldseqam;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +533,187 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
+	oldamid = seqrel->rd_rel->relam;
+	oldseqam = GetSequenceAMByAMId(oldamid);
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	if (stmt->accessMethod)
+		seqamid = get_seqam_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(stmt->options, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid == oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		HeapTuple       seqamtup;
+		Form_pg_am		form_am;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
+		SequenceAM	   *newseqam;
+
+		oldseqam = GetSequenceAMByAMId(oldamid);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = oldseqam->Alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
+
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(AMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
+
+		newseqam = GetSequenceAMByAMId(seqamid);
+
+		form_am = (Form_pg_am) GETSTRUCT(seqamtup);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		validnsps[0] = NameStr(form_am->amname);
+		validnsps[1] = NULL;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		(void) am_reloptions(newseqam->ParseRelOption, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-		PageSetLSN(page, recptr);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
+
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
+
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * And change the type definition.
+		 *
+		 * We can't use AlterTable internals here because the sequence
+		 * has to have the expected number of columns and no
+		 * attisdropped = true columns.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		replace_sequence_amstate_col(seqrelid, newseqam->StateTypeOid);
+		CommandCounterIncrement();
+
+		/* Rebuild the sequence tuple and save it. */
+		tuple = build_seq_tuple(seqrel, newseqam, new, restart_value);
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										AccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = AccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
+	else
+	{
+		Datum	   newamstate;
 
-	END_CRIT_SECTION();
+		sequence_check_range(restart_value, new->min_value, new->max_value,
+							 restart_requested ? "RESTART" : "START");
 
-	UnlockReleaseBuffer(buf);
+		/* Let the new sequence AM initialize. */
+		newamstate = oldseqam->Init(seqrel, new, restart_value,
+									restart_requested, false);
+
+		sequence_start_update(&seqh, true);
+		memcpy(seq, new, offsetof(FormData_pg_sequence, amstate));
+		sequence_save_state(&seqh, newamstate, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +754,26 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
+	SequenceAM *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSequenceAMForRelation(seqrel);
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +798,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam->Alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +815,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for syncrep.
-	 * It's sufficient to ensure the toplevel transaction has an xid, no need
-	 * to assign xids subxacts, that'll already trigger an appropriate wait.
-	 * (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +826,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +887,26 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
+	SequenceAM *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSequenceAMForRelation(seqrel);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,106 +923,86 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam->Setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	last_used_seq = elm;
 
-	UnlockReleaseBuffer(buf);
+	sequence_close(&seqh);
 
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	LocalSequenceState	state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+	SequenceAM *seqam;
+
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSequenceAMForRelation(seqrel);
+
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-	do_setval(relid, next, true);
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Build the state and pass it to sequence AM. */
+	state.last_value = next;
+	state.log_cnt = 0;
+	state.is_called = iscalled;
+	seqam->SetState(seqh.rel, &seqh, PointerGetDatum(&state));
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-	PG_RETURN_INT64(next);
-}
+	/* Reset local cached data */
+	elm->cached = elm->last;
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1034,11 +1061,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1116,58 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->statetyp = 6025; /* TODO */
+	seqh->statetyplen = -1;
+	seqh->statetypbyval = false;
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(!seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1177,170 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+Form_pg_sequence
+sequence_read_options(SequenceHandle *seqh)
+{
+	return (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
+}
+
+Datum
+sequence_read_state(SequenceHandle *seqh)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+
+	return PointerGetDatum(seq->amstate);
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool dowal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+
+	if (seqh->statetyplen < 0)
+	{
+		get_typlenbyval(seqh->statetyp, &seqh->statetyplen,
+						&seqh->statetypbyval);
+		Assert(seqh->statetyplen > 0);
+	}
+
+	if (dowal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_save_state(SequenceHandle *seqh, Datum amstate, bool dowal)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+	Page				page;
+
+	/*
+	 * Update the state data inline.
+	 *
+	 * This is only needed when the provided amstate datum points to different
+	 * data than what is already in the tuple.
+	 */
+	if (DatumGetPointer(amstate) != seq->amstate)
+	{
+		if (seqh->statetypbyval)
+			store_att_byval(seq->amstate, amstate, seqh->statetyplen);
+		else
+			memmove(seq->amstate, DatumGetPointer(amstate), seqh->statetyplen);
+	}
+
+	page = BufferGetPage(seqh->buf);
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (dowal && RelationNeedsWAL(seqh->rel))
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	Assert(BufferIsValid(seqh->buf));
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1350,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1427,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1435,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1444,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1452,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1459,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1472,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1525,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1539,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1649,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1677,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1685,85 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Datum		state;
+	char	   *statestr;
+	SequenceHandle seqh;
+	SequenceAM *seqam;
+	Oid			typoutput;
+	bool		typisvarlena;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSequenceAMForRelation(seqh.rel);
+
+	/* Get the type output function. */
+	getTypeOutputInfo(seqam->StateTypeOid, &typoutput, &typisvarlena);
+
+	/* Get the output and convert it to string. */
+	state = seqam->GetState(seqh.rel, &seqh);
+	statestr = OidOutputFunctionCall(typoutput, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_TEXT_P(cstring_to_text(statestr));
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *statestr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	SequenceHandle seqh;
+	SequenceAM *seqam;
+	Oid			typinput,
+				typioparam;
+	Datum		state;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSequenceAMForRelation(seqh.rel);
+
+	/* Get the type input function. */
+	getTypeInputInfo(seqam->StateTypeOid, &typinput, &typioparam);
+
+	/* Convert the string to the state type and set it as new state. */
+	state = OidInputFunctionCall(typinput, statestr, typioparam, -1);
+	seqam->SetState(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1829,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ba0a7ec..bcd2c32 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -267,6 +268,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -452,7 +454,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -471,7 +473,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -549,13 +550,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		SequenceAM *seqam;
+
+		Assert(relamid != InvalidOid);
+		seqam = GetSequenceAMByAMId(relamid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 NULL, true, false);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		(void) am_reloptions(seqam->ParseRelOption, reloptions, true);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -676,6 +693,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3306,7 +3324,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4306,6 +4324,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4349,8 +4370,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a table, view, or foreign table");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9401,7 +9422,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->amroutine->amoptions, newOptions, true);
+			(void) am_reloptions(rel->amroutine->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqam->ParseRelOption, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8d8731d..a706331 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2113,7 +2113,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index efa4be1..3cea360 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 4cf14b6..a4e235b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3556,7 +3556,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3569,7 +3571,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index a13d831..8fbf26d 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1606,7 +1606,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1617,7 +1619,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 223ef17..d23fc22 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3585,7 +3585,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3607,7 +3633,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3615,11 +3665,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3678,7 +3751,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 7f1c28f..82e3bce 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -29,6 +29,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -454,6 +455,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
@@ -1701,7 +1704,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_index_am_oid(DEFAULT_INDEX_TYPE,
+														 false))
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("index \"%s\" is not a btree", index_name),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index e81bbc6..e687caf 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -965,7 +965,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -998,7 +999,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 577c059..fed33c4 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -999,7 +999,7 @@ InitCatCachePhase2(CatCache *cache, bool touch_index)
 
 	if (touch_index &&
 		cache->id != AMOID &&
-		cache->id != AMNAME)
+		cache->id != AMNAMEKIND)
 	{
 		Relation	idesc;
 
@@ -1060,7 +1060,7 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 			break;
 
 		case AMOID:
-		case AMNAME:
+		case AMNAMEKIND:
 
 			/*
 			 * Always do heap scans in pg_am, because it's so small there's
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 7d19b45..5a2720a 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "access/transam.h"
 #include "access/xact.h"
@@ -258,6 +259,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -421,6 +423,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	amoptions_function	amoptions = NULL;
 
 	relation->rd_options = NULL;
 
@@ -429,10 +432,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->amroutine->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqam->ParseRelOption;
+			break;
 		default:
 			return;
 	}
@@ -443,9 +451,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 * code for pg_class itself)
 	 */
 	options = extractRelOptions(tuple,
-								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->amroutine->amoptions : NULL);
+								GetPgClassDescriptor(), amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1046,11 +1052,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1595,6 +1604,31 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	SequenceAM	   *result, *tmp;
+
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 rel->rd_rel->relam);
+	aform = (Form_pg_am) MemoryContextAlloc(CacheMemoryContext, sizeof *aform);
+	memcpy(aform, GETSTRUCT(tuple), sizeof *aform);
+	ReleaseSysCache(tuple);
+	rel->rd_am = aform;
+
+	result = (SequenceAM *) MemoryContextAlloc(CacheMemoryContext,
+											   sizeof(SequenceAM));
+	tmp = GetSequenceAM(rel->rd_am->amhandler);
+	memcpy(result, tmp, sizeof(SequenceAM));
+	rel->rd_seqam = result;
+}
 
 /*
  *		formrdesc
@@ -2081,6 +2115,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 		pfree(relation->rd_indextuple);
 	if (relation->rd_am)
 		pfree(relation->rd_am);
+	if (relation->rd_seqam)
+		pfree(relation->rd_seqam);
 	if (relation->rd_indexcxt)
 		MemoryContextDelete(relation->rd_indexcxt);
 	if (relation->rd_rulescxt)
@@ -4927,6 +4963,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqam == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index efce7b9..167ea03 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -126,12 +126,12 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		16
 	},
-	{AccessMethodRelationId,	/* AMNAME */
-		AmNameIndexId,
-		1,
+	{AccessMethodRelationId,	/* AMNAMEKIND */
+		AmNameKindIndexId,
+		2,
 		{
 			Anum_pg_am_amname,
-			0,
+			Anum_pg_am_amkind,
 			0,
 			0
 		},
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a185749..0509d25 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 36863df..52f2961 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -45,6 +45,7 @@
 #include "access/attnum.h"
 #include "access/sysattr.h"
 #include "access/transam.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_cast.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_default_acl.h"
@@ -4552,6 +4553,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4603,6 +4605,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4645,6 +4648,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4687,6 +4691,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4729,6 +4734,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4769,6 +4775,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4808,6 +4815,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4847,6 +4855,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4886,6 +4895,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4924,6 +4934,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4958,6 +4969,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4987,6 +4999,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5026,6 +5039,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5079,6 +5093,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5144,6 +5159,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -14956,7 +14975,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -15036,6 +15056,37 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.6 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.amname\n"
+								 "FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -15076,6 +15127,13 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -15142,6 +15200,9 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15158,16 +15219,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.6 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15180,14 +15254,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index cb88b82..580d28f 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -223,6 +223,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_clas) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index bb59bc2..9105cff 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1403,30 +1403,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1470,13 +1446,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1805,6 +1823,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1814,12 +1834,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.amname\n"
+								"FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 7de02d2..f54af37 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -273,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
+extern bytea *am_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..9edebee
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef bytea * (*ParseRelOptions_function) (Datum reloptions,
+											 bool validate);
+
+typedef Datum (*SeqAMInit_function) (Relation seqrel,
+									 Form_pg_sequence seq,
+									 int64 restart_value,
+									 bool restart_requested,
+									 bool is_init);
+
+typedef int64 (*SeqAMAlloc_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 nrequested,
+									  int64 *last);
+
+typedef void (*SeqAMSetval_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 new_value);
+
+typedef Datum (*SeqAMGetState_function) (Relation seqrel,
+										 SequenceHandle *seqh);
+typedef void (*SeqAMSetState_function) (Relation seqrel, SequenceHandle *seqh,
+										Datum amstate);
+
+typedef struct SequenceAM
+{
+	NodeTag		type;
+
+	/* Custom columns needed by the AM */
+	Oid			StateTypeOid;
+
+	/* Function for parsing reloptions */
+	ParseRelOptions_function	ParseRelOption;
+
+	/* Initialization */
+	SeqAMInit_function			Init;
+
+	/* nextval handler */
+	SeqAMAlloc_function			Alloc;
+
+	/* State manipulation functions */
+	SeqAMSetval_function		Setval;
+	SeqAMGetState_function		GetState;
+	SeqAMSetState_function		SetState;
+} SequenceAM;
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+extern SequenceAM *GetSequenceAM(Oid seqamhandler);
+extern SequenceAM *GetSequenceAMByAMId(Oid amoid);
+extern SequenceAM *GetSequenceAMForRelation(Relation relation);
+
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern Form_pg_sequence sequence_read_options(SequenceHandle *seqh);
+extern Datum sequence_read_state(SequenceHandle *seqh);
+extern void sequence_start_update(SequenceHandle *seqh, bool dowal);
+extern void sequence_save_state(SequenceHandle *seqh, Datum seqstate, bool dowal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+/* TODO move to seqamlocal.h */
+typedef struct LocalSequenceState
+{
+	int64   last_value;
+	int32	log_cnt;
+	bool	is_called;
+} LocalSequenceState;
+
+extern Datum seqam_local_state_in(PG_FUNCTION_ARGS);
+extern Datum seqam_local_state_out(PG_FUNCTION_ARGS);
+extern Datum seqam_local_handler(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index e6ac394..ce0ba57 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index c38958d..6a8eeb4 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -60,8 +60,8 @@ extern void CatalogUpdateIndexes(Relation heapRel, HeapTuple heapTuple);
 DECLARE_UNIQUE_INDEX(pg_aggregate_fnoid_index, 2650, on pg_aggregate using btree(aggfnoid oid_ops));
 #define AggregateFnoidIndexId  2650
 
-DECLARE_UNIQUE_INDEX(pg_am_name_index, 2651, on pg_am using btree(amname name_ops));
-#define AmNameIndexId  2651
+DECLARE_UNIQUE_INDEX(pg_am_name_kind_index, 2651, on pg_am using btree(amname name_ops, amkind char_ops));
+#define AmNameKindIndexId  2651
 DECLARE_UNIQUE_INDEX(pg_am_oid_index, 2652, on pg_am using btree(oid oid_ops));
 #define AmOidIndexId  2652
 
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index f19a873..ecc8812 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -34,6 +34,7 @@
 CATALOG(pg_am,2601)
 {
 	NameData	amname;			/* access method name */
+	char		amkind;			/* access method kind */
 	regproc		amhandler;		/* handler function */
 } FormData_pg_am;
 
@@ -48,32 +49,40 @@ 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_amkind				2
+#define Anum_pg_am_amhandler			3
 
 /* ----------------
  *		initial contents of pg_am
  * ----------------
  */
 
-DATA(insert OID = 403 (  btree		bthandler ));
+DATA(insert OID = 403 (  btree		i	bthandler ));
 DESCR("b-tree index access method");
 #define BTREE_AM_OID 403
-DATA(insert OID = 405 (  hash		hashhandler ));
+DATA(insert OID = 405 (  hash		i	hashhandler ));
 DESCR("hash index access method");
 #define HASH_AM_OID 405
-DATA(insert OID = 783 (  gist		gisthandler ));
+DATA(insert OID = 783 (  gist		i	gisthandler ));
 DESCR("GiST index access method");
 #define GIST_AM_OID 783
-DATA(insert OID = 2742 (  gin		ginhandler ));
+DATA(insert OID = 2742 (  gin		i	ginhandler ));
 DESCR("GIN index access method");
 #define GIN_AM_OID 2742
-DATA(insert OID = 4000 (  spgist	spghandler ));
+DATA(insert OID = 4000 (  spgist	i	spghandler ));
 DESCR("SP-GiST index access method");
 #define SPGIST_AM_OID 4000
-DATA(insert OID = 3580 (  brin		brinhandler ));
+DATA(insert OID = 3580 (  brin		i	brinhandler ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
+DATA(insert OID = 6022 (  local		S	seqam_local_handler ));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#define		  AMKIND_INDEX			  'i'		/* index */
+#define		  AMKIND_SEQUENCE		  'S'		/* sequence */
+
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b3c6915..57bfbfb 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1735,6 +1735,10 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v u 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 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 25 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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 s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3636,6 +3640,10 @@ DESCR("BERNOULLI tablesample method handler");
 DATA(insert OID = 3314 (  system			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_system_handler _null_ _null_ _null_ ));
 DESCR("SYSTEM tablesample method handler");
 
+/* sequence access method handlers */
+DATA(insert OID = 6023 (  seqam_local_handler	   PGNSP PGUID 12 1 0 0 0 f f f f f f v s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ _null_ seqam_local_handler _null_ _null_ _null_ ));
+DESCR("Local SequenceAM handler");
+
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
 DESCR("MD5 hash");
@@ -5181,6 +5189,11 @@ DESCR("brin handler");
 DATA(insert OID = 336 (  amvalidate	PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 16 "26" _null_ _null_ _null_ _null_ _null_	amvalidate _null_ _null_ _null_ ));
 DESCR("ask access method to validate opclass");
 
+/* Sequence AM */
+DATA(insert OID = 6027 (  seqam_local_state_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 6025 "2275" _null_ _null_ _null_ _null_ _null_ seqam_local_state_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6028 (  seqam_local_state_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6025" _null_ _null_ _null_ _null_ _null_ seqam_local_state_out _null_ _null_ _null_ ));
+DESCR("I/O");
 
 
 /*
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 59b04ac..9900c3d 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -701,6 +701,11 @@ DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 ind
 DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 #define ANYRANGEOID		3831
 
+/* Sequence AM composite types */
+DATA(insert OID = 6025 ( seqam_local_state	PGNSP PGUID 16 f b U f t \054 0 0 6026 seqam_local_state_in seqam_local_state_out - - - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQAMLOCALSTATEOID 6025
+DATA(insert OID = 6026 ( _seqam_local_state	PGNSP PGUID -1 f b A f t \054 0 6025 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 
 /*
  * macros
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 112f9dc..0493d8a 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -91,7 +91,7 @@ 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 Oid	get_index_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);
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 44862bb..7841527 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -19,20 +19,18 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "storage/relfilenode.h"
-
+#include "access/htup_details.h"
 
 typedef struct FormData_pg_sequence
 {
-	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
+	/* amstate follows */
+	char		amstate[FLEXIBLE_ARRAY_MEMBER];
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,19 +39,16 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  * Columns of a sequence relation
  */
 
-#define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_STARTVAL		1
+#define SEQ_COL_INCBY			2
+#define SEQ_COL_MAXVALUE		3
+#define SEQ_COL_MINVALUE		4
+#define SEQ_COL_CACHE			5
+#define SEQ_COL_CYCLE			6
+#define SEQ_COL_AMSTATE			7
 
-#define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_FIRSTCOL		SEQ_COL_STARTVAL
+#define SEQ_COL_LASTCOL			SEQ_COL_AMSTATE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +67,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index f269c63..cb621b1 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 0ad9ee9..58d0dce 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -457,7 +457,8 @@ typedef enum NodeTag
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_TsmRoutine,				/* in access/tsmapi.h */
-	T_IndexAmRoutine			/* in access/amapi.h */
+	T_IndexAmRoutine,			/* in access/amapi.h */
+	T_SequenceAM				/* in access/seqam.h */
 } NodeTag;
 
 /*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index abd4dd1..cd5db91 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2192,8 +2192,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2201,8 +2203,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 483b840..a8441d1 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -124,6 +124,9 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	/* These are non-NULL only for a sequence relation */
+	struct SequenceAM *rd_seqam;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
@@ -132,7 +135,7 @@ typedef struct RelationData
 	IndexAmRoutine *amroutine;
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 18404e2..7a666a5 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -32,7 +32,7 @@
 enum SysCacheIdentifier
 {
 	AGGFNOID = 0,
-	AMNAME,
+	AMNAMEKIND,
 	AMOID,
 	AMOPOPID,
 	AMOPSTRATEGY,
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ae50c94..f1b1fc2 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -129,8 +129,9 @@ NOTICE:  view "v12_temp" will be a temporary view
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 NOTICE:  view "v13_temp" will be a temporary view
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3a19cde 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate 
+-------------+--------------+---------------------+-----------+-------------+-----------+---------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (1,f,0)
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate  
+-------------+--------------+---------------------+-----------+-------------+-----------+----------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (2,t,31)
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 6c71371..97b12fb 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,52 @@ SELECT table_name, column_name, is_updatable
   FROM information_schema.columns
  WHERE table_name LIKE E'r_\\_view%'
  ORDER BY table_name, ordinal_position;
- table_name |  column_name  | is_updatable 
-------------+---------------+--------------
- ro_view1   | a             | NO
- ro_view1   | b             | NO
- ro_view10  | a             | NO
- ro_view11  | a             | NO
- ro_view11  | b             | NO
- ro_view12  | a             | NO
- ro_view13  | a             | NO
- ro_view13  | b             | NO
- ro_view17  | a             | NO
- ro_view17  | b             | NO
- ro_view18  | a             | NO
- ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
- ro_view19  | start_value   | NO
- ro_view19  | increment_by  | NO
- ro_view19  | max_value     | NO
- ro_view19  | min_value     | NO
- ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
- ro_view19  | is_cycled     | NO
- ro_view19  | is_called     | NO
- ro_view2   | a             | NO
- ro_view2   | b             | NO
- ro_view20  | a             | NO
- ro_view20  | b             | NO
- ro_view20  | g             | NO
- ro_view3   | ?column?      | NO
- ro_view4   | count         | NO
- ro_view5   | a             | NO
- ro_view5   | rank          | NO
- ro_view6   | a             | NO
- ro_view6   | b             | NO
- ro_view7   | a             | NO
- ro_view7   | b             | NO
- ro_view8   | a             | NO
- ro_view8   | b             | NO
- ro_view9   | a             | NO
- ro_view9   | b             | NO
- rw_view14  | ctid          | NO
- rw_view14  | a             | YES
- rw_view14  | b             | YES
- rw_view15  | a             | YES
- rw_view15  | upper         | NO
- rw_view16  | a             | YES
- rw_view16  | b             | YES
- rw_view16  | aa            | YES
-(46 rows)
+ table_name | column_name  | is_updatable 
+------------+--------------+--------------
+ ro_view1   | a            | NO
+ ro_view1   | b            | NO
+ ro_view10  | a            | NO
+ ro_view11  | a            | NO
+ ro_view11  | b            | NO
+ ro_view12  | a            | NO
+ ro_view13  | a            | NO
+ ro_view13  | b            | NO
+ ro_view17  | a            | NO
+ ro_view17  | b            | NO
+ ro_view18  | a            | NO
+ ro_view19  | start_value  | NO
+ ro_view19  | increment_by | NO
+ ro_view19  | max_value    | NO
+ ro_view19  | min_value    | NO
+ ro_view19  | cache_value  | NO
+ ro_view19  | is_cycled    | NO
+ ro_view19  | amstate      | NO
+ ro_view2   | a            | NO
+ ro_view2   | b            | NO
+ ro_view20  | a            | NO
+ ro_view20  | b            | NO
+ ro_view20  | g            | NO
+ ro_view3   | ?column?     | NO
+ ro_view4   | count        | NO
+ ro_view5   | a            | NO
+ ro_view5   | rank         | NO
+ ro_view6   | a            | NO
+ ro_view6   | b            | NO
+ ro_view7   | a            | NO
+ ro_view7   | b            | NO
+ ro_view8   | a            | NO
+ ro_view8   | b            | NO
+ ro_view9   | a            | NO
+ ro_view9   | b            | NO
+ rw_view14  | ctid         | NO
+ rw_view14  | a            | YES
+ rw_view14  | b            | YES
+ rw_view15  | a            | YES
+ rw_view15  | upper        | NO
+ rw_view16  | a            | YES
+ rw_view16  | b            | YES
+ rw_view16  | aa           | YES
+(43 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 58d361d..e967fff 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -131,8 +131,9 @@ CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..c2b1164 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
-- 
1.9.1

#90Craig Ringer
craig@2ndquadrant.com
In reply to: Petr Jelinek (#89)
Re: Sequence Access Method WIP

On 1 January 2016 at 07:51, Petr Jelinek <petr@2ndquadrant.com> wrote:

Other than that, this is based on the new am api by Alexander Korotkov
[1]. It extends it by adding another column called amkind to the pg_am
which can have either value "i" for index or "S" for sequence (same as
relkind in pg_class for those).

This patch will no longer apply after 65c5fcd353 (
http://github.com/postgres/postgres/commit/65c5fcd353) as outlined in
/messages/by-id/10804.1453077804@sss.pgh.pa.us .

Setting waiting-on-author in the CF app.

The good news is that the commit of the pg_am rework greatly eases the path
of this patch into core.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

#91Craig Ringer
craig@2ndquadrant.com
In reply to: Craig Ringer (#90)
Re: Sequence Access Method WIP

Needs rework after the commit of https://commitfest.postgresql.org/8/336/
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#92Petr Jelinek
petr@2ndquadrant.com
In reply to: Craig Ringer (#91)
1 attachment(s)
Re: Sequence Access Method WIP

On 18 January 2016 at 09:19, Craig Ringer <craig@2ndquadrant.com> wrote:

Needs rework after the commit of https://commitfest.postgresql.org/8/336/

Here is version that applies to current master. There is some work to
do (mostly cleanup) and the DDL is missing, but that's because I want
to know what people think about the principle of how it works now and
if it makes sense to finish it this way (explained in the original
submission for Jan CF).

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-seqam-2015-01-28.patchapplication/octet-stream; name=0001-seqam-2015-01-28.patchDownload
From 555abab39ed62050430efa2ca4cf38db8568ae92 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Tue, 26 Jan 2016 18:06:32 +0100
Subject: [PATCH] seqam 2015-01-28

---
 doc/src/sgml/ref/alter_sequence.sgml          |   55 +
 doc/src/sgml/ref/create_sequence.sgml         |   25 +
 src/backend/access/Makefile                   |    4 +-
 src/backend/access/common/reloptions.c        |   13 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqamapi.c        |  186 +++
 src/backend/access/sequence/seqlocal.c        |  398 +++++++
 src/backend/bootstrap/bootparse.y             |    1 +
 src/backend/catalog/heap.c                    |   17 +-
 src/backend/catalog/objectaddress.c           |    3 +-
 src/backend/catalog/toasting.c                |    1 +
 src/backend/commands/cluster.c                |    1 +
 src/backend/commands/createas.c               |    3 +-
 src/backend/commands/indexcmds.c              |   12 +-
 src/backend/commands/opclasscmds.c            |   15 +-
 src/backend/commands/sequence.c               | 1502 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |   36 +-
 src/backend/commands/typecmds.c               |    3 +-
 src/backend/commands/view.c                   |    3 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   77 +-
 src/backend/parser/parse_utilcmd.c            |    6 +-
 src/backend/tcop/utility.c                    |    6 +-
 src/backend/utils/adt/pseudotypes.c           |   12 +-
 src/backend/utils/cache/catcache.c            |    4 +-
 src/backend/utils/cache/relcache.c            |   51 +-
 src/backend/utils/cache/syscache.c            |    8 +-
 src/backend/utils/misc/guc.c                  |    1 +
 src/bin/pg_dump/pg_dump.c                     |  115 +-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/psql/describe.c                       |  112 +-
 src/include/access/reloptions.h               |    2 +-
 src/include/access/seqamapi.h                 |  110 ++
 src/include/catalog/heap.h                    |    1 +
 src/include/catalog/indexing.h                |    4 +-
 src/include/catalog/pg_am.h                   |   25 +-
 src/include/catalog/pg_proc.h                 |   17 +-
 src/include/catalog/pg_type.h                 |    7 +-
 src/include/commands/defrem.h                 |    2 +-
 src/include/commands/sequence.h               |   31 +-
 src/include/commands/tablecmds.h              |    2 +-
 src/include/nodes/nodes.h                     |    1 +
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/utils/builtins.h                  |    4 +-
 src/include/utils/rel.h                       |    8 +-
 src/include/utils/syscache.h                  |    2 +-
 src/test/regress/expected/create_view.out     |    5 +-
 src/test/regress/expected/opr_sanity.out      |    2 +-
 src/test/regress/expected/sequence.out        |   35 +-
 src/test/regress/expected/updatable_views.out |   95 +-
 src/test/regress/sql/create_view.sql          |    5 +-
 src/test/regress/sql/opr_sanity.sql           |    2 +-
 src/test/regress/sql/sequence.sql             |   10 +-
 54 files changed, 2291 insertions(+), 783 deletions(-)
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqamapi.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqamapi.h

diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a..223403d 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,7 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  tablesample transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc sequence \
+			  spgist tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 86b9ae1..e4f3825 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -888,8 +888,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
- * AM's options parser function in the case of a tuple corresponding to an
- * index, or NULL otherwise.
+ * or sequence AM's options parser function in the case of a tuple corresponding
+ * to an index or sequence respectively, or NULL otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
@@ -921,7 +921,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1374,14 +1375,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for access methods.
  *
- *	amoptions	index AM's option parser function
+ *	amoptions	AM's option parser function
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+am_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 {
 	Assert(amoptions != NULL);
 
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..1784799
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqamapi.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqamapi.c b/src/backend/access/sequence/seqamapi.c
new file mode 100644
index 0000000..1f3a72d
--- /dev/null
+++ b/src/backend/access/sequence/seqamapi.c
@@ -0,0 +1,186 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqamapi.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amstate.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_am.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * 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,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+	Oid			result;
+	HeapTuple	tuple;
+
+	/* look up the access method */
+	tuple = SearchSysCache2(AMNAMEKIND, PointerGetDatum(amname),
+							CharGetDatum(AMKIND_SEQUENCE));
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		result = HeapTupleGetOid(tuple);
+		ReleaseSysCache(tuple);
+	}
+	else
+		result = InvalidOid;
+
+	if (!OidIsValid(result) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" does not exist",
+						amname)));
+	return result;
+}
+
+/*
+ * GetSeqAmRoutine - call the specified access method handler routine
+ * to get its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutine(Oid amhandler)
+{
+	Datum			datum;
+	SeqAmRoutine	   *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SeqAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SeqAmRoutine))
+		elog(ERROR, "sequence access method handler function %u did not return an SeqAmRoutine struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * GetSeqAmRoutineByAMId - look up the handler of the access method
+ * for the given OID, and retrieve its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineByAMId(Oid amoid)
+{
+	HeapTuple		tuple;
+	regproc			amhandler;
+	Form_pg_am		amform;
+
+	/* 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);
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("sequence access method \"%s\" does not have a handler",
+						NameStr(amform->amname))));
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function. */
+	return GetSeqAmRoutine(amhandler);
+}
+
+
+SeqAmRoutine *
+GetSeqAmRoutineForRelation(Relation relation)
+{
+	SeqAmRoutine *seqam;
+	SeqAmRoutine *cseqam;
+
+	if (relation->rd_seqamroutine == NULL)
+	{
+		if (!OidIsValid(relation->rd_rel->relam))
+		{
+			char *relname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(relation)),
+													   RelationGetRelationName(relation));
+			elog(ERROR, "relation %s isn't sequence", relname);
+		}
+
+		seqam = GetSeqAmRoutineByAMId(relation->rd_rel->relam);
+		/* Save the data for later reuse in CacheMemoryContext */
+		cseqam = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												   sizeof(SeqAmRoutine));
+		memcpy(cseqam, seqam, sizeof(SeqAmRoutine));
+		relation->rd_seqamroutine = cseqam;
+	}
+
+	return relation->rd_seqamroutine;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..5fb9b98
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,398 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+#include "utils/typcache.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+static bytea *seqam_local_reloptions(Datum reloptions, bool validate);
+static Datum seqam_local_init(Relation seqrel, Form_pg_sequence seq,
+							  int64 restart_value, bool restart_requested,
+							  bool is_init);
+static int64 seqam_local_alloc(Relation seqrel, SequenceHandle *seqh,
+							   int64 nrequested, int64 *last);
+static void seqam_local_setval(Relation seqrel, SequenceHandle *seqh,
+							   int64 new_value);
+static Datum seqam_local_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+								  Datum amstate);
+
+static char *
+seqam_local_state_get_part(StringInfo buf, char *ptr, bool *found)
+{
+	*found = false;
+
+	resetStringInfo(buf);
+
+	while (*ptr != ',' && *ptr != ')')
+	{
+		char		ch = *ptr++;
+
+		if (ch == '\0')
+		{
+			*found = false;
+			return ptr;
+		}
+
+		appendStringInfoChar(buf, ch);
+
+		if (!*found)
+			*found = true;
+	}
+
+	return ptr;
+}
+
+Datum
+seqam_local_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr;
+	Datum		d;
+	bool		found;
+	StringInfoData		buf;
+	LocalSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	initStringInfo(&buf);
+
+	/* last_value is required */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (!found)
+		goto malformed;
+	d = DirectFunctionCall1(int8in, CStringGetDatum(buf.data));
+	state->last_value = DatumGetInt64(d);
+	if (*ptr == ',')
+		ptr++;
+
+	/* is_called is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(boolin, CStringGetDatum(buf.data));
+		state->is_called = DatumGetBool(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->is_called = true;
+
+	/* log_cnt is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(int4in, CStringGetDatum(buf.data));
+		state->log_cnt = DatumGetInt32(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->log_cnt = 0;
+
+	if (*ptr != ')' || *(++ptr) != '\0')
+		goto malformed;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_local_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_local_state_out(PG_FUNCTION_ARGS)
+{
+	LocalSequenceState *state = (LocalSequenceState *) PG_GETARG_POINTER(0);
+	StringInfoData		buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s,%d)",
+					 state->last_value, state->is_called ? "t" : "f",
+					 state->log_cnt);
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+Datum
+seqam_local_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine *seqam = makeNode(SeqAmRoutine);
+
+	seqam->StateTypeOid = SEQAMLOCALSTATEOID;
+	seqam->amoptions = seqam_local_reloptions;
+	seqam->Init = seqam_local_init;
+	seqam->Alloc = seqam_local_alloc;
+	seqam->Setval = seqam_local_setval;
+	seqam->GetState = seqam_local_get_state;
+	seqam->SetState = seqam_local_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+static bytea *
+seqam_local_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+static Datum
+seqam_local_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	LocalSequenceState	   *localseq;
+
+	if (is_init)
+		localseq = palloc0(sizeof(LocalSequenceState));
+	else
+		localseq = (LocalSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		localseq->last_value = restart_value;
+		localseq->is_called = false;
+	}
+
+	localseq->log_cnt = 0;
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+static int64
+seqam_local_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+				  int64 *last)
+{
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) seq->amstate;
+
+	next = result = localseq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = localseq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_start_update(seqh, logit);
+
+	if (logit)
+	{
+		localseq->last_value = next;
+		localseq->log_cnt = 0;
+		localseq->is_called = true;
+
+		sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	}
+
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_save_state(seqh, PointerGetDatum(localseq), false);
+
+	sequence_finish_update(seqh);
+
+	return result;
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+static void
+seqam_local_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq;
+
+	amstate = sequence_read_state(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	localseq->last_value = new_value;		/* last fetched number */
+	localseq->is_called = true;
+	localseq->log_cnt = 0;			/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+static Datum
+seqam_local_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq = palloc(sizeof(LocalSequenceState));
+
+	amstate = sequence_read_state(seqh);
+	memcpy(localseq, DatumGetPointer(amstate), sizeof(LocalSequenceState));
+	sequence_release_tuple(seqh);
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+static void
+seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+					  Datum amstate)
+{
+	(void) sequence_read_state(seqh);
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 41d2fd4..e684f60 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -251,6 +251,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d14cbb7..cfceb34 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -41,6 +41,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
@@ -87,6 +88,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -848,6 +850,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -921,6 +924,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1034,6 +1038,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1262,6 +1267,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1274,7 +1280,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the pg_am record.
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1329,6 +1336,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = AccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 0232e0d..abc34e8 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
@@ -1488,7 +1489,7 @@ get_object_address_opcf(ObjectType objtype, List *objname, bool missing_ok)
 	ObjectAddress address;
 
 	/* XXX no missing_ok support here */
-	amoid = get_am_oid(strVal(linitial(objname)), false);
+	amoid = get_index_am_oid(strVal(linitial(objname)), false);
 	objname = list_copy_tail(objname, 1);
 
 	switch (objtype)
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index f40a005..40b5e91 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -291,6 +291,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5cb28cf..54a22da 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -680,6 +680,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index fcb0331..5537d44 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 13b04e6..6721582 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -155,7 +155,8 @@ CheckIndexCompatible(Oid oldId,
 	Assert(numberOfAttributes <= INDEX_MAX_KEYS);
 
 	/* look up the access method */
-	tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
+	tuple = SearchSysCache2(AMNAMEKIND, PointerGetDatum(accessMethodName),
+							CharGetDatum(AMKIND_INDEX));
 	if (!HeapTupleIsValid(tuple))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -472,7 +473,8 @@ DefineIndex(Oid relationId,
 	 * look up the access method, verify it can handle the requested features
 	 */
 	accessMethodName = stmt->accessMethod;
-	tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
+	tuple = SearchSysCache2(AMNAMEKIND, PointerGetDatum(accessMethodName),
+							CharGetDatum(AMKIND_INDEX));
 	if (!HeapTupleIsValid(tuple))
 	{
 		/*
@@ -484,7 +486,9 @@ DefineIndex(Oid relationId,
 			ereport(NOTICE,
 					(errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
 			accessMethodName = "gist";
-			tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
+			tuple = SearchSysCache2(AMNAMEKIND,
+									PointerGetDatum(accessMethodName),
+									CharGetDatum(AMKIND_INDEX));
 		}
 
 		if (!HeapTupleIsValid(tuple))
@@ -536,7 +540,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 8a66196..47d7b05 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -354,7 +354,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
 					   get_namespace_name(namespaceoid));
 
 	/* Get necessary info about access method */
-	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
+	tup = SearchSysCache2(AMNAMEKIND, CStringGetDatum(stmt->amname),
+						  CharGetDatum(AMKIND_INDEX));
 	if (!HeapTupleIsValid(tup))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -743,7 +744,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
 					   get_namespace_name(namespaceoid));
 
 	/* Get access method OID, throwing an error if it doesn't exist. */
-	amoid = get_am_oid(stmt->amname, false);
+	amoid = get_index_am_oid(stmt->amname, false);
 
 	/* XXX Should we make any privilege check against the AM? */
 
@@ -780,7 +781,8 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
 	IndexAmRoutine *amroutine;
 
 	/* Get necessary info about access method */
-	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
+	tup = SearchSysCache2(AMNAMEKIND, CStringGetDatum(stmt->amname),
+						  CharGetDatum(AMKIND_INDEX));
 	if (!HeapTupleIsValid(tup))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1725,17 +1727,18 @@ IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
 }
 
 /*
- * get_am_oid - given an access method name, look up the OID
+ * get_index_am_oid - given an index 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)
+get_index_am_oid(const char *amname, bool missing_ok)
 {
 	Oid			oid;
 
-	oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
+	oid = GetSysCacheOid2(AMNAMEKIND, CStringGetDatum(amname),
+						  CharGetDatum(AMKIND_INDEX));
 	if (!OidIsValid(oid) && !missing_ok)
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..b1c87d2 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,16 +14,22 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/tupmacs.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +42,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,24 +82,112 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	Oid			statetyp;
+	int16		statetyplen;
+	bool		statetypbyval;
+	HeapTupleData tup;
+	bool		inupdate;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
  */
 static SeqTableData *last_used_seq = NULL;
 
+static HeapTuple build_seq_tuple(Relation rel, SeqAmRoutine *seqam,
+								 Form_pg_sequence new, int64 restart_value);
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+
+/*
+ * Build template column definition for a sequence relation.
+*/
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_not_null = true;
+	coldef->is_from_type = false;
+	/* Force plain storage. */
+	coldef->storage = 'p';
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(Oid amstateTypeOid)
+{
+	List	   *seqcols;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		switch (colid)
+		{
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+			case SEQ_COL_AMSTATE:
+				coldef->typeName = makeTypeNameFromOid(amstateTypeOid, -1);
+				coldef->colname = "amstate";
+				break;
+		}
+
+		seqcols = lappend(seqcols, coldef);
+	}
 
+	return seqcols;
+}
 
 /*
  * DefineSequence
@@ -111,14 +200,12 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
+	List	   *seqcols;
+	SeqAmRoutine *seqam;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -147,102 +234,33 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
-	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
-
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
-		{
-			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
-				break;
-			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
-				break;
-			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
-				break;
-			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
-				break;
-			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
-				break;
-			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
-			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
-				break;
-		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
-	}
+	if (seq->accessMethod)
+		seqamid = get_seqam_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
+	seqam = GetSeqAmRoutineByAMId(seqamid);
+	/* Build column definitions. */
+	seqcols = BuildSeqColumnDefList(seqam->StateTypeOid);
 
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
+	stmt->tableElts = seqcols;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	/* Build new sequence tuple and store it. */
+	tuple = build_seq_tuple(rel, seqam, &new, new.start_value);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +272,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +286,89 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
+	TupleDesc			tupDesc;
+	Datum	   values[SEQ_COL_LASTCOL];
+	bool	   nulls[SEQ_COL_LASTCOL];
+	SeqAmRoutine *seqam;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
-	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tupDesc = RelationGetDescr(seqrel);
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(seqrel, seq, seq->start_value,
+											  true, false);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
+}
+
+/*
+ * Build sequence tuple based on the sequence form and fill in the
+ * sequence AM specific info as well.
+ */
+static HeapTuple
+build_seq_tuple(Relation rel, SeqAmRoutine *seqam, Form_pg_sequence new,
+				int64 restart_value)
+{
+	TupleDesc	tupDesc;
+	HeapTuple	tuple;
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+
+	tupDesc = RelationGetDescr(rel);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new->start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new->increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new->max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new->min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new->cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new->is_cycled);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(rel, new, restart_value,
+											  false, true);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	return tuple;
 }
 
 /*
@@ -361,7 +411,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +431,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,6 +439,69 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 }
 
 /*
+ * Replace the type of amstate column.
+ *
+ * We don't do AlterTable here as that produces dead columns which we don't
+ * want. This is safe because the sequence page is controlled by code in this
+ * module and isn't changed the same way as a table.
+ *
+ * TODO: check if anybody is depending on the type associated with the
+ * sequence.
+ */
+static void
+replace_sequence_amstate_col(Oid seqrelid, Oid typid)
+{
+	Relation	attr_rel;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	HeapTuple	tp,
+				attr_tuple,
+				newattr_tuple;
+	Form_pg_type	typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+
+	memset(nulls, 0, sizeof(nulls));
+	memset(replace, 0, sizeof(replace));
+
+	replace[Anum_pg_attribute_atttypid - 1] = true;
+	replace[Anum_pg_attribute_attlen - 1] = true;
+	replace[Anum_pg_attribute_attbyval - 1] = true;
+	replace[Anum_pg_attribute_attalign - 1] = true;
+
+	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(typid);
+	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(typtup->typlen);
+	values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(typtup->typbyval);
+	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(typtup->typalign);
+
+	/* Build DROP command for amstate of old AM. */
+	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+	attr_tuple = SearchSysCache2(ATTNUM,
+								 ObjectIdGetDatum(seqrelid),
+								 Int16GetDatum(SEQ_COL_AMSTATE));
+	if (!HeapTupleIsValid(attr_tuple))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 SEQ_COL_AMSTATE, seqrelid);
+
+	newattr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel),
+									  values, nulls, replace);
+	simple_heap_update(attr_rel, &newattr_tuple->t_self, newattr_tuple);
+	CatalogUpdateIndexes(attr_rel, newattr_tuple);
+
+	ReleaseSysCache(tp);
+	heap_freetuple(newattr_tuple);
+	ReleaseSysCache(attr_tuple);
+
+	heap_close(attr_rel, RowExclusiveLock);
+}
+
+/*
  * AlterSequence
  *
  * Modify the definition of a sequence relation
@@ -406,19 +509,24 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence seq,
+					 new;
 	List	   *owned_by;
 	ObjectAddress address;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
+	SeqAmRoutine *oldseqam;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +534,187 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
+	oldamid = seqrel->rd_rel->relam;
+	oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	if (stmt->accessMethod)
+		seqamid = get_seqam_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(stmt->options, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid == oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		HeapTuple       seqamtup;
+		Form_pg_am		form_am;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
+		SeqAmRoutine	   *newseqam;
+
+		oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = oldseqam->Alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
+
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(AMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
+
+		newseqam = GetSeqAmRoutineByAMId(seqamid);
+
+		form_am = (Form_pg_am) GETSTRUCT(seqamtup);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		validnsps[0] = NameStr(form_am->amname);
+		validnsps[1] = NULL;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		(void) am_reloptions(newseqam->amoptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-		PageSetLSN(page, recptr);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
+
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
+
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * And change the type definition.
+		 *
+		 * We can't use AlterTable internals here because the sequence
+		 * has to have the expected number of columns and no
+		 * attisdropped = true columns.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		replace_sequence_amstate_col(seqrelid, newseqam->StateTypeOid);
+		CommandCounterIncrement();
+
+		/* Rebuild the sequence tuple and save it. */
+		tuple = build_seq_tuple(seqrel, newseqam, new, restart_value);
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										AccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = AccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
+	else
+	{
+		Datum	   newamstate;
 
-	END_CRIT_SECTION();
+		sequence_check_range(restart_value, new->min_value, new->max_value,
+							 restart_requested ? "RESTART" : "START");
 
-	UnlockReleaseBuffer(buf);
+		/* Let the new sequence AM initialize. */
+		newamstate = oldseqam->Init(seqrel, new, restart_value,
+									restart_requested, false);
+
+		sequence_start_update(&seqh, true);
+		memcpy(seq, new, offsetof(FormData_pg_sequence, amstate));
+		sequence_save_state(&seqh, newamstate, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +755,26 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +799,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam->Alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +816,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for syncrep.
-	 * It's sufficient to ensure the toplevel transaction has an xid, no need
-	 * to assign xids subxacts, that'll already trigger an appropriate wait.
-	 * (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +827,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +888,26 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,106 +924,86 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam->Setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	last_used_seq = elm;
 
-	UnlockReleaseBuffer(buf);
+	sequence_close(&seqh);
 
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	LocalSequenceState	state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
+
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-	do_setval(relid, next, true);
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Build the state and pass it to sequence AM. */
+	state.last_value = next;
+	state.log_cnt = 0;
+	state.is_called = iscalled;
+	seqam->SetState(seqh.rel, &seqh, PointerGetDatum(&state));
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-	PG_RETURN_INT64(next);
-}
+	/* Reset local cached data */
+	elm->cached = elm->last;
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1034,11 +1062,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1117,58 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->statetyp = 6025; /* TODO */
+	seqh->statetyplen = -1;
+	seqh->statetypbyval = false;
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(!seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1178,170 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+Form_pg_sequence
+sequence_read_options(SequenceHandle *seqh)
+{
+	return (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
+}
+
+Datum
+sequence_read_state(SequenceHandle *seqh)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+
+	return PointerGetDatum(seq->amstate);
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool dowal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+
+	if (seqh->statetyplen < 0)
+	{
+		get_typlenbyval(seqh->statetyp, &seqh->statetyplen,
+						&seqh->statetypbyval);
+		Assert(seqh->statetyplen > 0);
+	}
+
+	if (dowal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_save_state(SequenceHandle *seqh, Datum amstate, bool dowal)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+	Page				page;
+
+	/*
+	 * Update the state data inline.
+	 *
+	 * This is only needed when the provided amstate datum points to different
+	 * data than what is already in the tuple.
+	 */
+	if (DatumGetPointer(amstate) != seq->amstate)
+	{
+		if (seqh->statetypbyval)
+			store_att_byval(seq->amstate, amstate, seqh->statetyplen);
+		else
+			memmove(seq->amstate, DatumGetPointer(amstate), seqh->statetyplen);
+	}
+
+	page = BufferGetPage(seqh->buf);
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (dowal && RelationNeedsWAL(seqh->rel))
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	Assert(BufferIsValid(seqh->buf));
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1351,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1428,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1436,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1445,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1453,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1460,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1473,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1526,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1540,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1650,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1678,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1686,85 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Datum		state;
+	char	   *statestr;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typoutput;
+	bool		typisvarlena;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type output function. */
+	getTypeOutputInfo(seqam->StateTypeOid, &typoutput, &typisvarlena);
+
+	/* Get the output and convert it to string. */
+	state = seqam->GetState(seqh.rel, &seqh);
+	statestr = OidOutputFunctionCall(typoutput, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_TEXT_P(cstring_to_text(statestr));
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *statestr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typinput,
+				typioparam;
+	Datum		state;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type input function. */
+	getTypeInputInfo(seqam->StateTypeOid, &typinput, &typioparam);
+
+	/* Convert the string to the state type and set it as new state. */
+	state = OidInputFunctionCall(typinput, statestr, typioparam, -1);
+	seqam->SetState(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1830,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index eeda3b4..cb7d177 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -453,7 +454,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -472,7 +473,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -550,13 +550,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		SeqAmRoutine *seqam;
+
+		Assert(relamid != InvalidOid);
+		seqam = GetSeqAmRoutineByAMId(relamid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 NULL, true, false);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		(void) am_reloptions(seqam->amoptions, reloptions, true);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -677,6 +693,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -9402,7 +9419,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqamroutine->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 4f41766..bd40104 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2114,7 +2114,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e9d9ba2..ca0f481 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5877037..65c738b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3558,7 +3558,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3571,7 +3573,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 08ccc0d..1ba5e15 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1606,7 +1606,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1617,7 +1619,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b307b48..e6263cb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3585,7 +3585,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3607,7 +3633,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3615,11 +3665,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3678,7 +3751,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index a65b2977..70fae4a 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -29,6 +29,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -455,6 +456,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
@@ -1708,7 +1711,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_index_am_oid(DEFAULT_INDEX_TYPE,
+														 false))
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("index \"%s\" is not a btree", index_name),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 045f7f0..1e57f39 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -965,7 +965,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -998,7 +999,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index dd447cf..e2e4572 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -374,27 +374,27 @@ fdw_handler_out(PG_FUNCTION_ARGS)
 
 
 /*
- * index_am_handler_in		- input routine for pseudo-type INDEX_AM_HANDLER.
+ * am_handler_in		- input routine for pseudo-type AM_HANDLER.
  */
 Datum
-index_am_handler_in(PG_FUNCTION_ARGS)
+am_handler_in(PG_FUNCTION_ARGS)
 {
 	ereport(ERROR,
 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			 errmsg("cannot accept a value of type index_am_handler")));
+			 errmsg("cannot accept a value of type am_handler")));
 
 	PG_RETURN_VOID();			/* keep compiler quiet */
 }
 
 /*
- * index_am_handler_out		- output routine for pseudo-type INDEX_AM_HANDLER.
+ * am_handler_out		- output routine for pseudo-type AM_HANDLER.
  */
 Datum
-index_am_handler_out(PG_FUNCTION_ARGS)
+am_handler_out(PG_FUNCTION_ARGS)
 {
 	ereport(ERROR,
 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			 errmsg("cannot display a value of type index_am_handler")));
+			 errmsg("cannot display a value of type am_handler")));
 
 	PG_RETURN_VOID();			/* keep compiler quiet */
 }
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index e929616..cacc0dc 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -999,7 +999,7 @@ InitCatCachePhase2(CatCache *cache, bool touch_index)
 
 	if (touch_index &&
 		cache->id != AMOID &&
-		cache->id != AMNAME)
+		cache->id != AMNAMEKIND)
 	{
 		Relation	idesc;
 
@@ -1060,7 +1060,7 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 			break;
 
 		case AMOID:
-		case AMNAME:
+		case AMNAMEKIND:
 
 			/*
 			 * Always do heap scans in pg_am, because it's so small there's
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 130c06d..31dcb4e 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -260,6 +261,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -424,6 +426,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	amoptions_function	amoptions = NULL;
 
 	relation->rd_options = NULL;
 
@@ -432,10 +435,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_amroutine->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqamroutine->amoptions;
+			break;
 		default:
 			return;
 	}
@@ -1049,11 +1057,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1232,6 +1243,7 @@ RelationInitIndexAccessInfo(Relation relation)
 		elog(ERROR, "cache lookup failed for access method %u",
 			 relation->rd_rel->relam);
 	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amkind == AMKIND_INDEX);
 	relation->rd_amhandler = aform->amhandler;
 	ReleaseSysCache(tuple);
 
@@ -1555,6 +1567,34 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation relation)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	SeqAmRoutine	   *result, *tmp;
+
+	/*
+	 * Look up the index's access method, save the OID of its handler function
+	 */
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 relation->rd_rel->relam);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amkind == AMKIND_SEQUENCE);
+	relation->rd_amhandler = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	result = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												 sizeof(SeqAmRoutine));
+	tmp = GetSeqAmRoutine(relation->rd_amhandler);
+	memcpy(result, tmp, sizeof(SeqAmRoutine));
+	relation->rd_seqamroutine = result;
+}
 
 /*
  *		formrdesc
@@ -4885,6 +4925,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqam == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 65ffe84..238c02f 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -127,12 +127,12 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		16
 	},
-	{AccessMethodRelationId,	/* AMNAME */
-		AmNameIndexId,
-		1,
+	{AccessMethodRelationId,	/* AMNAMEKIND */
+		AmNameKindIndexId,
+		2,
 		{
 			Anum_pg_am_amname,
-			0,
+			Anum_pg_am_amkind,
 			0,
 			0
 		},
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 38ba82f..eae858f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqamapi.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index de1ece1..974291d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -45,6 +45,7 @@
 #include "access/attnum.h"
 #include "access/sysattr.h"
 #include "access/transam.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_cast.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_default_acl.h"
@@ -4649,6 +4650,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4700,6 +4702,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4742,6 +4745,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4784,6 +4788,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4826,6 +4831,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4866,6 +4872,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4905,6 +4912,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4944,6 +4952,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -4983,6 +4992,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5021,6 +5031,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5055,6 +5066,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5084,6 +5096,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5123,6 +5136,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5176,6 +5190,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5241,6 +5256,10 @@ getTables(Archive *fout, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -15106,7 +15125,8 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -15186,6 +15206,37 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.6 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.amname\n"
+								 "FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -15226,6 +15277,13 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -15292,6 +15350,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15308,16 +15369,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.6 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15330,14 +15404,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 78b2584..2b4be8d 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -223,6 +223,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_class) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 85e3aa5..15a102c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1403,30 +1403,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1470,13 +1446,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1805,6 +1823,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1814,12 +1834,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.amname\n"
+								"FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 469ac67..69135aa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -273,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
+extern bytea *am_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqamapi.h b/src/include/access/seqamapi.h
new file mode 100644
index 0000000..dccabd4
--- /dev/null
+++ b/src/include/access/seqamapi.h
@@ -0,0 +1,110 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqamapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAMAPI_H
+#define SEQAMAPI_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef bytea * (*ParseRelOptions_function) (Datum reloptions,
+											 bool validate);
+
+typedef Datum (*SeqAMInit_function) (Relation seqrel,
+									 Form_pg_sequence seq,
+									 int64 restart_value,
+									 bool restart_requested,
+									 bool is_init);
+
+typedef int64 (*SeqAMAlloc_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 nrequested,
+									  int64 *last);
+
+typedef void (*SeqAMSetval_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 new_value);
+
+typedef Datum (*SeqAMGetState_function) (Relation seqrel,
+										 SequenceHandle *seqh);
+typedef void (*SeqAMSetState_function) (Relation seqrel, SequenceHandle *seqh,
+										Datum amstate);
+
+typedef struct SeqAmRoutine
+{
+	NodeTag		type;
+
+	/* Custom columns needed by the AM */
+	Oid			StateTypeOid;
+
+	/* Function for parsing reloptions */
+	ParseRelOptions_function	amoptions;
+
+	/* Initialization */
+	SeqAMInit_function			Init;
+
+	/* nextval handler */
+	SeqAMAlloc_function			Alloc;
+
+	/* State manipulation functions */
+	SeqAMSetval_function		Setval;
+	SeqAMGetState_function		GetState;
+	SeqAMSetState_function		SetState;
+} SeqAmRoutine;
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+extern SeqAmRoutine *GetSeqAmRoutine(Oid seqamhandler);
+extern SeqAmRoutine *GetSeqAmRoutineByAMId(Oid amoid);
+extern SeqAmRoutine *GetSeqAmRoutineForRelation(Relation relation);
+
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern Form_pg_sequence sequence_read_options(SequenceHandle *seqh);
+extern Datum sequence_read_state(SequenceHandle *seqh);
+extern void sequence_start_update(SequenceHandle *seqh, bool dowal);
+extern void sequence_save_state(SequenceHandle *seqh, Datum seqstate,
+								bool dowal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+/* We need this public for serval3 */
+typedef struct LocalSequenceState
+{
+	int64   last_value;
+	int32	log_cnt;
+	bool	is_called;
+} LocalSequenceState;
+
+extern Datum seqam_local_state_in(PG_FUNCTION_ARGS);
+extern Datum seqam_local_state_out(PG_FUNCTION_ARGS);
+extern Datum seqam_local_handler(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAMAPI_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..d6b3cd7 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ab2c1a8..72acff2 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -60,8 +60,8 @@ extern void CatalogUpdateIndexes(Relation heapRel, HeapTuple heapTuple);
 DECLARE_UNIQUE_INDEX(pg_aggregate_fnoid_index, 2650, on pg_aggregate using btree(aggfnoid oid_ops));
 #define AggregateFnoidIndexId  2650
 
-DECLARE_UNIQUE_INDEX(pg_am_name_index, 2651, on pg_am using btree(amname name_ops));
-#define AmNameIndexId  2651
+DECLARE_UNIQUE_INDEX(pg_am_name_kind_index, 2651, on pg_am using btree(amname name_ops, amkind char_ops));
+#define AmNameKindIndexId  2651
 DECLARE_UNIQUE_INDEX(pg_am_oid_index, 2652, on pg_am using btree(oid oid_ops));
 #define AmOidIndexId  2652
 
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index f801c3e..3bb275a 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -34,6 +34,7 @@
 CATALOG(pg_am,2601)
 {
 	NameData	amname;			/* access method name */
+	char		amkind;			/* access method kind */
 	regproc		amhandler;		/* handler function */
 } FormData_pg_am;
 
@@ -48,32 +49,40 @@ 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_amkind				2
+#define Anum_pg_am_amhandler			3
 
 /* ----------------
  *		initial contents of pg_am
  * ----------------
  */
 
-DATA(insert OID = 403 (  btree		bthandler ));
+DATA(insert OID = 403 (  btree		i	bthandler ));
 DESCR("b-tree index access method");
 #define BTREE_AM_OID 403
-DATA(insert OID = 405 (  hash		hashhandler ));
+DATA(insert OID = 405 (  hash		i	hashhandler ));
 DESCR("hash index access method");
 #define HASH_AM_OID 405
-DATA(insert OID = 783 (  gist		gisthandler ));
+DATA(insert OID = 783 (  gist		i	gisthandler ));
 DESCR("GiST index access method");
 #define GIST_AM_OID 783
-DATA(insert OID = 2742 (  gin		ginhandler ));
+DATA(insert OID = 2742 (  gin		i	ginhandler ));
 DESCR("GIN index access method");
 #define GIN_AM_OID 2742
-DATA(insert OID = 4000 (  spgist	spghandler ));
+DATA(insert OID = 4000 (  spgist	i	spghandler ));
 DESCR("SP-GiST index access method");
 #define SPGIST_AM_OID 4000
-DATA(insert OID = 3580 (  brin		brinhandler ));
+DATA(insert OID = 3580 (  brin		i	brinhandler ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
+DATA(insert OID = 6022 (  local		S	seqam_local_handler ));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#define		  AMKIND_INDEX			  'i'		/* index */
+#define		  AMKIND_SEQUENCE		  'S'		/* sequence */
+
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 3a066ab..e112b20 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1751,6 +1751,10 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v u 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 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 25 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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 s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3661,9 +3665,9 @@ DATA(insert OID = 3116 (  fdw_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s
 DESCR("I/O");
 DATA(insert OID = 3117 (  fdw_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3115" _null_ _null_ _null_ _null_ _null_ fdw_handler_out _null_ _null_ _null_ ));
 DESCR("I/O");
-DATA(insert OID = 326  (  index_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 325 "2275" _null_ _null_ _null_ _null_ _null_ index_am_handler_in _null_ _null_ _null_ ));
+DATA(insert OID = 326  (  am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 325 "2275" _null_ _null_ _null_ _null_ _null_ am_handler_in _null_ _null_ _null_ ));
 DESCR("I/O");
-DATA(insert OID = 327  (  index_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
+DATA(insert OID = 327  (  am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ am_handler_out _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 3311 (  tsm_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3676,6 +3680,10 @@ DESCR("BERNOULLI tablesample method handler");
 DATA(insert OID = 3314 (  system			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_system_handler _null_ _null_ _null_ ));
 DESCR("SYSTEM tablesample method handler");
 
+/* sequence access method handlers */
+DATA(insert OID = 6023 (  seqam_local_handler	   PGNSP PGUID 12 1 0 0 0 f f f f f f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ seqam_local_handler _null_ _null_ _null_ ));
+DESCR("Local SequenceAM handler");
+
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
 DESCR("MD5 hash");
@@ -5206,6 +5214,11 @@ DESCR("get an individual replication origin's replication progress");
 DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v r 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
 DESCR("get progress for all replication origins");
 
+/* Sequence AM */
+DATA(insert OID = 6027 (  seqam_local_state_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 6025 "2275" _null_ _null_ _null_ _null_ _null_ seqam_local_state_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6028 (  seqam_local_state_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6025" _null_ _null_ _null_ _null_ _null_ seqam_local_state_out _null_ _null_ _null_ ));
+DESCR("I/O");
 
 /*
  * Symbolic values for provolatile column: these indicate whether the result
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2c90b76..c07259d 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -694,13 +694,18 @@ DATA(insert OID = 3500 ( anyenum		PGNSP PGUID  4 t p P f t \054 0 0 0 anyenum_in
 #define ANYENUMOID		3500
 DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define FDW_HANDLEROID	3115
-DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 325 ( am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 am_handler_in am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define INDEX_AM_HANDLEROID	325
 DATA(insert OID = 3310 ( tsm_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define TSM_HANDLEROID	3310
 DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 #define ANYRANGEOID		3831
 
+/* Sequence AM composite types */
+DATA(insert OID = 6025 ( seqam_local_state	PGNSP PGUID 16 f b U f t \054 0 0 6026 seqam_local_state_in seqam_local_state_out - - - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQAMLOCALSTATEOID 6025
+DATA(insert OID = 6026 ( _seqam_local_state	PGNSP PGUID -1 f b A f t \054 0 6025 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 
 /*
  * macros
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 54f67e9..899e112 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -91,7 +91,7 @@ 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 Oid	get_index_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);
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..e0f6439 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -19,20 +19,18 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "storage/relfilenode.h"
-
+#include "access/htup_details.h"
 
 typedef struct FormData_pg_sequence
 {
-	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
+	/* amstate follows */
+	char		amstate[FLEXIBLE_ARRAY_MEMBER];
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,19 +39,16 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  * Columns of a sequence relation
  */
 
-#define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_STARTVAL		1
+#define SEQ_COL_INCBY			2
+#define SEQ_COL_MAXVALUE		3
+#define SEQ_COL_MINVALUE		4
+#define SEQ_COL_CACHE			5
+#define SEQ_COL_CYCLE			6
+#define SEQ_COL_AMSTATE			7
 
-#define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_FIRSTCOL		SEQ_COL_STARTVAL
+#define SEQ_COL_LASTCOL			SEQ_COL_AMSTATE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +67,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 7a770f4..1bceec8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index cf09db4..5fdb487 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -455,6 +455,7 @@ typedef enum NodeTag
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
+	T_SeqAmRoutine,				/* in access/seqamapi.h */
 	T_TsmRoutine				/* in access/tsmapi.h */
 } NodeTag;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2fd0629..ff74978 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2192,8 +2192,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2201,8 +2203,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index c2e529f..66ec708 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -574,8 +574,8 @@ extern Datum language_handler_in(PG_FUNCTION_ARGS);
 extern Datum language_handler_out(PG_FUNCTION_ARGS);
 extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
 extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
-extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
-extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
+extern Datum am_handler_in(PG_FUNCTION_ARGS);
+extern Datum am_handler_out(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
 extern Datum internal_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f2bebf2..4fe7e52 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -106,13 +106,18 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	Oid			rd_amhandler;	/* OID of access method's handler function */
+
+	/* These are non-NULL only for a sequence relation */
+	struct SeqAmRoutine *rd_seqamroutine;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
@@ -126,7 +131,6 @@ typedef struct RelationData
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
-	Oid			rd_amhandler;	/* OID of index AM's handler function */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
 	/* use "struct" here to avoid needing to include amapi.h: */
 	struct IndexAmRoutine *rd_amroutine;		/* index AM's API struct */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 256615b..d401780 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -32,7 +32,7 @@
 enum SysCacheIdentifier
 {
 	AGGFNOID = 0,
-	AMNAME,
+	AMNAMEKIND,
 	AMOID,
 	AMOPOPID,
 	AMOPSTRATEGY,
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ae50c94..f1b1fc2 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -129,8 +129,9 @@ NOTICE:  view "v12_temp" will be a temporary view
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 NOTICE:  view "v13_temp" will be a temporary view
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 7c09fa3..24b3584 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1586,7 +1586,7 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype != 'am_handler'::regtype OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
  oid | amname | oid | proname 
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3a19cde 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate 
+-------------+--------------+---------------------+-----------+-------------+-----------+---------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (1,f,0)
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate  
+-------------+--------------+---------------------+-----------+-------------+-----------+----------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (2,t,31)
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 6c71371..97b12fb 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,52 @@ SELECT table_name, column_name, is_updatable
   FROM information_schema.columns
  WHERE table_name LIKE E'r_\\_view%'
  ORDER BY table_name, ordinal_position;
- table_name |  column_name  | is_updatable 
-------------+---------------+--------------
- ro_view1   | a             | NO
- ro_view1   | b             | NO
- ro_view10  | a             | NO
- ro_view11  | a             | NO
- ro_view11  | b             | NO
- ro_view12  | a             | NO
- ro_view13  | a             | NO
- ro_view13  | b             | NO
- ro_view17  | a             | NO
- ro_view17  | b             | NO
- ro_view18  | a             | NO
- ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
- ro_view19  | start_value   | NO
- ro_view19  | increment_by  | NO
- ro_view19  | max_value     | NO
- ro_view19  | min_value     | NO
- ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
- ro_view19  | is_cycled     | NO
- ro_view19  | is_called     | NO
- ro_view2   | a             | NO
- ro_view2   | b             | NO
- ro_view20  | a             | NO
- ro_view20  | b             | NO
- ro_view20  | g             | NO
- ro_view3   | ?column?      | NO
- ro_view4   | count         | NO
- ro_view5   | a             | NO
- ro_view5   | rank          | NO
- ro_view6   | a             | NO
- ro_view6   | b             | NO
- ro_view7   | a             | NO
- ro_view7   | b             | NO
- ro_view8   | a             | NO
- ro_view8   | b             | NO
- ro_view9   | a             | NO
- ro_view9   | b             | NO
- rw_view14  | ctid          | NO
- rw_view14  | a             | YES
- rw_view14  | b             | YES
- rw_view15  | a             | YES
- rw_view15  | upper         | NO
- rw_view16  | a             | YES
- rw_view16  | b             | YES
- rw_view16  | aa            | YES
-(46 rows)
+ table_name | column_name  | is_updatable 
+------------+--------------+--------------
+ ro_view1   | a            | NO
+ ro_view1   | b            | NO
+ ro_view10  | a            | NO
+ ro_view11  | a            | NO
+ ro_view11  | b            | NO
+ ro_view12  | a            | NO
+ ro_view13  | a            | NO
+ ro_view13  | b            | NO
+ ro_view17  | a            | NO
+ ro_view17  | b            | NO
+ ro_view18  | a            | NO
+ ro_view19  | start_value  | NO
+ ro_view19  | increment_by | NO
+ ro_view19  | max_value    | NO
+ ro_view19  | min_value    | NO
+ ro_view19  | cache_value  | NO
+ ro_view19  | is_cycled    | NO
+ ro_view19  | amstate      | NO
+ ro_view2   | a            | NO
+ ro_view2   | b            | NO
+ ro_view20  | a            | NO
+ ro_view20  | b            | NO
+ ro_view20  | g            | NO
+ ro_view3   | ?column?     | NO
+ ro_view4   | count        | NO
+ ro_view5   | a            | NO
+ ro_view5   | rank         | NO
+ ro_view6   | a            | NO
+ ro_view6   | b            | NO
+ ro_view7   | a            | NO
+ ro_view7   | b            | NO
+ ro_view8   | a            | NO
+ ro_view8   | b            | NO
+ ro_view9   | a            | NO
+ ro_view9   | b            | NO
+ rw_view14  | ctid         | NO
+ rw_view14  | a            | YES
+ rw_view14  | b            | YES
+ rw_view15  | a            | YES
+ rw_view15  | upper        | NO
+ rw_view16  | a            | YES
+ rw_view16  | b            | YES
+ rw_view16  | aa           | YES
+(43 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 58d361d..e967fff 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -131,8 +131,9 @@ CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 6c9784a..61977a9 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1055,7 +1055,7 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype != 'am_handler'::regtype OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
 
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..c2b1164 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
-- 
2.7.0

#93Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Petr Jelinek (#92)
Re: Sequence Access Method WIP

Petr Jelinek wrote:

On 18 January 2016 at 09:19, Craig Ringer <craig@2ndquadrant.com> wrote:

Needs rework after the commit of https://commitfest.postgresql.org/8/336/

Here is version that applies to current master. There is some work to
do (mostly cleanup) and the DDL is missing, but that's because I want
to know what people think about the principle of how it works now and
if it makes sense to finish it this way (explained in the original
submission for Jan CF).

I would guess that the DDL boilterplate should come from Alexander
Korotkov's patch, right? I think a first easy step may be to combine
parts both patches so that we get the "amkind" column from this patch
and the DDL support from Alexander's patch (means that his proposed
command needs a new clause to specify the amkind); then the really tough
stuff in both Alexander's and this patch can be rebased on top of that.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#94Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#93)
Re: Sequence Access Method WIP

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

I would guess that the DDL boilterplate should come from Alexander
Korotkov's patch, right? I think a first easy step may be to combine
parts both patches so that we get the "amkind" column from this patch
and the DDL support from Alexander's patch (means that his proposed
command needs a new clause to specify the amkind);

Uh, what? Surely we would provide a bespoke command for each possible
sort of handler. As an example, CREATE INDEX ACCESS METHOD ought to check
that the provided function has the right signature, and then it would put
the correct amkind into the pg_am entry automatically.

If we skimp on this infrastructure then we're just relying on the
user not to make a typo, which doesn't seem like a good idea.

(Agreed though that a reasonable first step would be to add amkind to
pg_am and make the appropriate adjustments to existing code, ie check
that amkind is correct when fetching an index handler. I considered
putting that into the AM API refactor patch, but desisted.)

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#95Petr Jelinek
petr@2ndquadrant.com
In reply to: Tom Lane (#94)
Re: Sequence Access Method WIP

On 29 January 2016 at 14:48, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

I would guess that the DDL boilterplate should come from Alexander
Korotkov's patch, right? I think a first easy step may be to combine
parts both patches so that we get the "amkind" column from this patch
and the DDL support from Alexander's patch (means that his proposed
command needs a new clause to specify the amkind);

Uh, what? Surely we would provide a bespoke command for each possible
sort of handler. As an example, CREATE INDEX ACCESS METHOD ought to check
that the provided function has the right signature, and then it would put
the correct amkind into the pg_am entry automatically.

If we skimp on this infrastructure then we're just relying on the
user not to make a typo, which doesn't seem like a good idea.

Yeah it has to be completely separate command for both that do
completely different sanity checks. It can't even be called CREATE
INDEX/SEQUENCE ACCESS METHOD unless we are willing to make ACCESS a
keyword due to preexisting CREATE INDEX/SEQUENCE <name> commands. The
previous version of the patch which used somewhat different underlying
catalog structure already had DDL and honestly writing the DDL part is
quite easy. I used CREATE ACCESS METHOD FOR SEQUENCE there.

(Agreed though that a reasonable first step would be to add amkind to
pg_am and make the appropriate adjustments to existing code, ie check
that amkind is correct when fetching an index handler. I considered
putting that into the AM API refactor patch, but desisted.)

Well I was wondering how to handle this as well, the submitted patch
currently just adds Asserts, because IMHO the actual ERROR should be
thrown in the DDL not in the code that just uses it.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#96Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Petr Jelinek (#95)
Re: Sequence Access Method WIP

Petr Jelinek wrote:

On 29 January 2016 at 14:48, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Uh, what? Surely we would provide a bespoke command for each possible
sort of handler. As an example, CREATE INDEX ACCESS METHOD ought to check
that the provided function has the right signature, and then it would put
the correct amkind into the pg_am entry automatically.

I'm thinking we'd do CREATE ACCESS METHOD foobar TYPE INDEX or something
like that.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#97Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#96)
Re: Sequence Access Method WIP

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

On 29 January 2016 at 14:48, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Uh, what? Surely we would provide a bespoke command for each possible
sort of handler. As an example, CREATE INDEX ACCESS METHOD ought to check
that the provided function has the right signature, and then it would put
the correct amkind into the pg_am entry automatically.

I'm thinking we'd do CREATE ACCESS METHOD foobar TYPE INDEX or something
like that.

That seems okay. I had the impression you were proposing
CREATE ACCESS METHOD foobar TYPE 'i' USING functionname
or something like that, where it would be totally up to the user that
the amkind matched the function. That seems unnecessarily error-prone,
not to mention that it would close the door forever on any hope that
we might allow non-superusers to execute such commands.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#98Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Alvaro Herrera (#96)
Re: Sequence Access Method WIP

On Fri, Jan 29, 2016 at 6:36 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

Petr Jelinek wrote:

On 29 January 2016 at 14:48, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Uh, what? Surely we would provide a bespoke command for each possible
sort of handler. As an example, CREATE INDEX ACCESS METHOD ought to

check

that the provided function has the right signature, and then it would

put

the correct amkind into the pg_am entry automatically.

I'm thinking we'd do CREATE ACCESS METHOD foobar TYPE INDEX or something
like that.

Ok! Let's nail down the syntax and I can integrate it into my createam
patch.
I would prefer "CREATE {INDEX | SEQUENCE | ... } ACCESS METHOD name HANDLER
handler;", but I don't insist.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#99Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alexander Korotkov (#98)
Re: Sequence Access Method WIP

Alexander Korotkov <a.korotkov@postgrespro.ru> writes:

On Fri, Jan 29, 2016 at 6:36 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

I'm thinking we'd do CREATE ACCESS METHOD foobar TYPE INDEX or something
like that.

I would prefer "CREATE {INDEX | SEQUENCE | ... } ACCESS METHOD name HANDLER
handler;", but I don't insist.

I think that Alvaro's idea is less likely to risk future grammar
conflicts. We've had enough headaches from CREATE [ UNIQUE ] INDEX
[ CONCURRENTLY ] to make me really wary of variables in the statement-name
part of the syntax.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#100Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#99)
Re: Sequence Access Method WIP

On Fri, Jan 29, 2016 at 5:24 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alexander Korotkov <a.korotkov@postgrespro.ru> writes:

On Fri, Jan 29, 2016 at 6:36 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

I'm thinking we'd do CREATE ACCESS METHOD foobar TYPE INDEX or something
like that.

I would prefer "CREATE {INDEX | SEQUENCE | ... } ACCESS METHOD name HANDLER
handler;", but I don't insist.

I think that Alvaro's idea is less likely to risk future grammar
conflicts. We've had enough headaches from CREATE [ UNIQUE ] INDEX
[ CONCURRENTLY ] to make me really wary of variables in the statement-name
part of the syntax.

Strong +1. If we put the type of access method immediately after
CREATE, I'm almost positive we'll regret it for exactly that reason.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#101Petr Jelinek
petr@2ndquadrant.com
In reply to: Robert Haas (#100)
Re: Sequence Access Method WIP

On 29 January 2016 at 23:59, Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Jan 29, 2016 at 5:24 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alexander Korotkov <a.korotkov@postgrespro.ru> writes:

On Fri, Jan 29, 2016 at 6:36 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

I'm thinking we'd do CREATE ACCESS METHOD foobar TYPE INDEX or something
like that.

I would prefer "CREATE {INDEX | SEQUENCE | ... } ACCESS METHOD name HANDLER
handler;", but I don't insist.

I think that Alvaro's idea is less likely to risk future grammar
conflicts. We've had enough headaches from CREATE [ UNIQUE ] INDEX
[ CONCURRENTLY ] to make me really wary of variables in the statement-name
part of the syntax.

Strong +1. If we put the type of access method immediately after
CREATE, I'm almost positive we'll regret it for exactly that reason.

Just as a note, CREATE SEQUENCE ACCESS METHOD already causes grammar
conflict now, that's why my proposal was different, I didn't want to
add more keywords. I think Alvaro's proposal is fine as well.

The other point is that we are creating ACCESS METHOD object so that's
what should be after CREATE.

In any case this is slightly premature IMHO as DDL is somewhat unless
until we have sequence access methods implementation we can agree on,
or the generic WAL logging so that custom indexes can be crash safe.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#102Robert Haas
robertmhaas@gmail.com
In reply to: Petr Jelinek (#101)
Re: Sequence Access Method WIP

On Sat, Jan 30, 2016 at 7:37 AM, Petr Jelinek <petr@2ndquadrant.com> wrote:

Just as a note, CREATE SEQUENCE ACCESS METHOD already causes grammar
conflict now, that's why my proposal was different, I didn't want to
add more keywords. I think Alvaro's proposal is fine as well.

I missed your proposal, I guess, so please don't take as having any
position on whether it's better or worse than Alvaro's. I was only
intending to vote for the proposition that the type of access method
should follow the name of the access method.

The other point is that we are creating ACCESS METHOD object so that's
what should be after CREATE.

Agreed.

In any case this is slightly premature IMHO as DDL is somewhat unless
until we have sequence access methods implementation we can agree on,
or the generic WAL logging so that custom indexes can be crash safe.

Generic WAL logging seems like a *great* idea to me. But I think it's
largely independent from the access method stuff. If we have generic
WAL logging, people can create WAL-logged stuff that is not a new
access method. If we have access methods, they can create new access
methods that are not WAL-logged. If we have both, then they can
create WAL-logged access methods which of course is the payoff pitch,
but I don't see it as necessary or desirable for the two systems to be
tied together in any way.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#103Petr Jelinek
petr@2ndquadrant.com
In reply to: Robert Haas (#102)
Re: Sequence Access Method WIP

On 30 January 2016 at 13:48, Robert Haas <robertmhaas@gmail.com> wrote:

On Sat, Jan 30, 2016 at 7:37 AM, Petr Jelinek <petr@2ndquadrant.com> wrote:

Just as a note, CREATE SEQUENCE ACCESS METHOD already causes grammar
conflict now, that's why my proposal was different, I didn't want to
add more keywords. I think Alvaro's proposal is fine as well.

I missed your proposal, I guess, so please don't take as having any
position on whether it's better or worse than Alvaro's. I was only
intending to vote for the proposition that the type of access method
should follow the name of the access method.

No worries didn't mean it that way.

In any case this is slightly premature IMHO as DDL is somewhat unless
until we have sequence access methods implementation we can agree on,
or the generic WAL logging so that custom indexes can be crash safe.

Generic WAL logging seems like a *great* idea to me. But I think it's
largely independent from the access method stuff. If we have generic
WAL logging, people can create WAL-logged stuff that is not a new
access method. If we have access methods, they can create new access
methods that are not WAL-logged. If we have both, then they can
create WAL-logged access methods which of course is the payoff pitch,
but I don't see it as necessary or desirable for the two systems to be
tied together in any way.

Okay, I am only debating the usefulness of DDL for access methods in
the current situation where we only have custom index access methods
which can't create WAL records. In other words trying to nudge people
slightly back towards the actual patch(es).

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#104Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Petr Jelinek (#101)
Re: Sequence Access Method WIP

On Sat, Jan 30, 2016 at 3:37 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

On 29 January 2016 at 23:59, Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Jan 29, 2016 at 5:24 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alexander Korotkov <a.korotkov@postgrespro.ru> writes:

On Fri, Jan 29, 2016 at 6:36 PM, Alvaro Herrera <

alvherre@2ndquadrant.com>

wrote:

I'm thinking we'd do CREATE ACCESS METHOD foobar TYPE INDEX or

something

like that.

I would prefer "CREATE {INDEX | SEQUENCE | ... } ACCESS METHOD name

HANDLER

handler;", but I don't insist.

I think that Alvaro's idea is less likely to risk future grammar
conflicts. We've had enough headaches from CREATE [ UNIQUE ] INDEX
[ CONCURRENTLY ] to make me really wary of variables in the

statement-name

part of the syntax.

Strong +1. If we put the type of access method immediately after
CREATE, I'm almost positive we'll regret it for exactly that reason.

Just as a note, CREATE SEQUENCE ACCESS METHOD already causes grammar
conflict now, that's why my proposal was different, I didn't want to
add more keywords. I think Alvaro's proposal is fine as well.

The other point is that we are creating ACCESS METHOD object so that's
what should be after CREATE.

In any case this is slightly premature IMHO as DDL is somewhat unless
until we have sequence access methods implementation we can agree on,
or the generic WAL logging so that custom indexes can be crash safe.

I've changed syntax of CREATE ACCESS METHOD syntax in the thread about
index access method extendability.
Now it is "CREATE ACCESS METHOD name TYPE INDEX HANDLER handler;". New
column amtype of pg_am stores access method type.
/messages/by-id/CAPpHfdu9gLN7kuicweGsp50CaAMWx8Q-JWzbPehc92rvFHzkeg@mail.gmail.com
It could be easily extended to "CREATE ACCESS METHOD name TYPE SEQUENCE
HANDLER handler;".

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#105Petr Jelinek
petr@2ndquadrant.com
In reply to: Alexander Korotkov (#104)
3 attachment(s)
Re: Sequence Access Method WIP

On 16/02/16 13:58, Alexander Korotkov wrote:

I've changed syntax of CREATE ACCESS METHOD syntax in the thread about
index access method extendability.
Now it is "CREATE ACCESS METHOD name TYPE INDEX HANDLER handler;". New
column amtype of pg_am stores access method type.
/messages/by-id/CAPpHfdu9gLN7kuicweGsp50CaAMWx8Q-JWzbPehc92rvFHzkeg@mail.gmail.com
It could be easily extended to "CREATE ACCESS METHOD name TYPE SEQUENCE
HANDLER handler;".

Yes I've seen and I will send a review within couple of days.

But first here is updated patch for sequence access methods. I went with
the previously discussed custom type as this gives us proper control
over the width of the state and making sure that it's not gonna be
toastable, etc and we need this as sequence is limited to single page.

Attached are 3 separate files. The first one (0001-create-am) is mainly
separate for the reason that there is some overlap with Alexander's
index access methods patch, so I extracted common code into separate
patch as it will make it easier to merge in case we are lucky enough to
get these patches in (but it's still based on Alexander's code). It
provides infra for defining new access methods from SQL, although
without the parser and without any actual access methods.

The 0002-seqam provides the SQL interface and support for sequence
access methods on top of the infra patch, and also provides all the
sequence access methods infrastructure and abstraction and documentation.

And finally the 0003-gapless-seq is example contrib module that
implements dependably and transitionally safe gapless sequence access
method. It's obviously slow as it has to do locking and basically
serialize all the changes to sequence so only one transaction may use it
at a time but it's truly gapless. It also serves as an example of use of
the api and as a test.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-create-am-infra-2015-03-05.patchapplication/x-patch; name=0001-create-am-infra-2015-03-05.patchDownload
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

0002-seqam-2015-03-05.patchapplication/x-patch; name=0002-seqam-2015-03-05.patchDownload
From 40e1438006a92ff56a226198df5dae5524cc88ef Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Wed, 2 Mar 2016 16:15:33 +0100
Subject: [PATCH 2/3] seqam

---
 doc/src/sgml/filelist.sgml                    |    1 +
 doc/src/sgml/postgres.sgml                    |    1 +
 doc/src/sgml/ref/allfiles.sgml                |    2 +
 doc/src/sgml/ref/alter_sequence.sgml          |   55 +
 doc/src/sgml/ref/create_access_method.sgml    |  116 ++
 doc/src/sgml/ref/create_sequence.sgml         |   25 +
 doc/src/sgml/ref/drop_access_method.sgml      |  112 ++
 doc/src/sgml/reference.sgml                   |    2 +
 doc/src/sgml/seqam.sgml                       |  312 +++++
 src/backend/access/Makefile                   |    4 +-
 src/backend/access/common/reloptions.c        |   13 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqamapi.c        |  191 ++++
 src/backend/access/sequence/seqlocal.c        |  408 +++++++
 src/backend/bootstrap/bootparse.y             |    1 +
 src/backend/catalog/heap.c                    |   17 +-
 src/backend/catalog/objectaddress.c           |    1 +
 src/backend/catalog/toasting.c                |    1 +
 src/backend/commands/amcmds.c                 |   14 +
 src/backend/commands/cluster.c                |    1 +
 src/backend/commands/createas.c               |    3 +-
 src/backend/commands/indexcmds.c              |    2 +-
 src/backend/commands/sequence.c               | 1502 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |   49 +-
 src/backend/commands/typecmds.c               |    3 +-
 src/backend/commands/view.c                   |    3 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |  136 ++-
 src/backend/parser/parse_utilcmd.c            |    3 +
 src/backend/tcop/utility.c                    |    6 +-
 src/backend/utils/adt/pseudotypes.c           |   25 +
 src/backend/utils/cache/relcache.c            |   53 +-
 src/backend/utils/misc/guc.c                  |    1 +
 src/bin/pg_dump/pg_dump.c                     |  141 ++-
 src/bin/pg_dump/pg_dump.h                     |    2 +
 src/bin/pg_dump/pg_dump_sort.c                |    3 +-
 src/bin/psql/describe.c                       |  112 +-
 src/include/access/reloptions.h               |    2 +-
 src/include/access/seqamapi.h                 |  109 ++
 src/include/catalog/heap.h                    |    1 +
 src/include/catalog/pg_am.h                   |    6 +
 src/include/catalog/pg_proc.h                 |   18 +
 src/include/catalog/pg_type.h                 |    7 +
 src/include/commands/sequence.h               |   31 +-
 src/include/commands/tablecmds.h              |    2 +-
 src/include/nodes/nodes.h                     |    1 +
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/parser/kwlist.h                   |    1 +
 src/include/utils/builtins.h                  |    2 +
 src/include/utils/rel.h                       |    8 +-
 src/test/regress/expected/create_am.out       |  127 +++
 src/test/regress/expected/create_view.out     |    5 +-
 src/test/regress/expected/opr_sanity.out      |    4 +-
 src/test/regress/expected/sequence.out        |   35 +-
 src/test/regress/expected/updatable_views.out |   95 +-
 src/test/regress/sql/create_am.sql            |   89 ++
 src/test/regress/sql/create_view.sql          |    5 +-
 src/test/regress/sql/opr_sanity.sql           |    4 +-
 src/test/regress/sql/sequence.sql             |   10 +-
 60 files changed, 3161 insertions(+), 755 deletions(-)
 create mode 100644 doc/src/sgml/ref/create_access_method.sgml
 create mode 100644 doc/src/sgml/ref/drop_access_method.sgml
 create mode 100644 doc/src/sgml/seqam.sgml
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqamapi.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqamapi.h
 create mode 100644 src/test/regress/expected/create_am.out
 create mode 100644 src/test/regress/sql/create_am.sql

diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index a12fee7..7b10c67 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 7e82cdc..d37058a 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -251,6 +251,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index bf95453..77667bd 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -52,6 +52,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY commit             SYSTEM "commit.sgml">
 <!ENTITY commitPrepared     SYSTEM "commit_prepared.sgml">
 <!ENTITY copyTable          SYSTEM "copy.sgml">
+<!ENTITY createAccessMethod SYSTEM "create_access_method.sgml">
 <!ENTITY createAggregate    SYSTEM "create_aggregate.sgml">
 <!ENTITY createCast         SYSTEM "create_cast.sgml">
 <!ENTITY createCollation    SYSTEM "create_collation.sgml">
@@ -94,6 +95,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY delete             SYSTEM "delete.sgml">
 <!ENTITY discard            SYSTEM "discard.sgml">
 <!ENTITY do                 SYSTEM "do.sgml">
+<!ENTITY dropAccessMethod   SYSTEM "drop_access_method.sgml">
 <!ENTITY dropAggregate      SYSTEM "drop_aggregate.sgml">
 <!ENTITY dropCast           SYSTEM "drop_cast.sgml">
 <!ENTITY dropCollation      SYSTEM "drop_collation.sgml">
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
new file mode 100644
index 0000000..4fe0fbd
--- /dev/null
+++ b/doc/src/sgml/ref/create_access_method.sgml
@@ -0,0 +1,116 @@
+<!--
+doc/src/sgml/ref/create_access_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATEACCESSMETHOD">
+ <indexterm zone="sql-createaccessmethod">
+  <primary>CREATE ACCESS METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>CREATE ACCESS METHOD</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE ACCESS METHOD</refname>
+  <refpurpose>define a new access method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
+    TYPE SEQUENCE
+    HANDLER <replaceable class="parameter">handler_function</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>CREATE ACCESS METHOD</command> creates a new access method.
+  </para>
+
+  <para>
+   The access method name must be unique within the database.
+  </para>
+
+  <para>
+   Only superusers can define new access methods.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the access method to be created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>TYPE SEQUENCE</literal></term>
+    <listitem>
+     <para>
+      This clause specifies type of access method to define.
+      For now, there are only sequence access methods.  But eventually
+      there would be other types of access methods.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
+    <listitem>
+     <para><replaceable class="parameter">handler_function</replaceable> is the
+      name of a previously registered function that will be called to
+      retrieve the struct which contains required parameters and functions
+      of access method to the core.  The handler function must take single
+      argument of type <type>internal</>. For a sequence access methods its
+      return type must be <type>sequence_am_handler</type>.
+     </para>
+
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Create an access method <literal>gapless</> with
+   handler function <literal>seqam_gapless_handler</>:
+<programlisting>
+CREATE ACCESS METHOD gapless TYPE SEQUENCE HANDLER seqam_gapless_handler;
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>CREATE ACCESS METHOD</command> is a
+   <productname>PostgreSQL</> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="seqam"></member>
+   <member><xref linkend="sql-dropaccessmethod"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/drop_access_method.sgml b/doc/src/sgml/ref/drop_access_method.sgml
new file mode 100644
index 0000000..a565d5f
--- /dev/null
+++ b/doc/src/sgml/ref/drop_access_method.sgml
@@ -0,0 +1,112 @@
+<!--
+doc/src/sgml/ref/drop_access_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DROPACCESSMETHOD">
+ <indexterm zone="sql-dropaccessmethod">
+  <primary>DROP ACCESS METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>DROP ACCESS METHOD</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP ACCESS METHOD</refname>
+  <refpurpose>remove an access method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP ACCESS METHOD [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP ACCESS METHOD</command> removes an existing access method.
+   Only superusers can drop access methods.
+  </para>
+
+  <para>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the access method does not exist.
+      A notice is issued in this case.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CASCADE</literal></term>
+    <listitem>
+     <para>
+      Automatically drop objects that depend on the access method (such as
+      sequences using it).
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+    <listitem>
+     <para>
+      Refuse to drop the access method if any objects depend on it.
+      This is the default.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Drop the access method <literal>gapless</>:
+<programlisting>
+DROP ACCESS METHOD gapless;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>DROP ACCESS METHOD</command> is a
+   <productname>PostgreSQL</> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createaccessmethod"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 03020df..8acdff1 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -80,6 +80,7 @@
    &commit;
    &commitPrepared;
    &copyTable;
+   &createAccessMethod;
    &createAggregate;
    &createCast;
    &createCollation;
@@ -122,6 +123,7 @@
    &delete;
    &discard;
    &do;
+   &dropAccessMethod;
    &dropAggregate;
    &dropCast;
    &dropCollation;
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..5faeae3
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,312 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_am</structname> system catalog (see
+   <xref linkend="catalog-pg-am">).
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The SQL interface of the sequence access method consists of a handler
+   function which has to return special pseudo-type
+   <type>sequence_handler</type>. This function must be written in a compiled
+   language such as C, using the version-1 interface. For details on C
+   language calling conventions and dynamic loading, see
+   <xref linkend="xfunc-c">.
+  </para>
+
+  <para>
+   The actual C structure returned by the handler function is following:
+<programlisting>
+typedef struct SeqAmRoutine
+{
+    NodeTag        type;
+
+    /* Custom datatype used for storing state of the sequence by the AM */
+    Oid            StateTypeOid;
+
+    /* Function for parsing reloptions */
+    ParseRelOptions_function    amoptions;
+
+    /* Initialization */
+    SeqAMInit_function          Init;
+
+    /* nextval handler */
+    SeqAMAlloc_function         Alloc;
+
+    /* State manipulation functions */
+    SeqAMSetval_function        Setval;
+    SeqAMGetState_function      GetState;
+    SeqAMSetState_function      SetState;
+} SeqAmRoutine;
+</programlisting>
+  </para>
+
+  <para>
+   These are the functions implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+Datum
+SeqAMInit (Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+           bool restart_requested, bool is_init)
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   The <literal>seq</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the Datum of a state of the sequence. The type
+   of the Datum is the <structname>StateTypeOid</structname> type.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+ParseRelOptions (Datum reloptions, bool validate)
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+SeqAMAlloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+            int64 *last)
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum SeqAMGetState (Relation seqrel, SequenceHandle *seqh)
+</programlisting>
+   Dump the current state of the sequence. The return value is the Datum of
+   a <structname>StateTypeOid</structname>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetState (Relation seqrel, SequenceHandle *seqh, Datum amstate)
+</programlisting>
+   Restore state of the sequence based on amstate which is Datum of a
+   <structname>StateTypeOid</structname> type.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following helper functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh)
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+Form_pg_sequence
+(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns the whole pg_sequence tuple as the
+   <structname>Form_pg_sequence</structname> structure.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum
+sequence_read_state(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns just the sequence access method specific state Datum.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_save_state(SequenceHandle *seqh, Datum seqstate, bool dowal)
+</programlisting>
+   Save the modified sequence state tuple indicating if the change should be
+   WAL logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh)
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_save_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh)
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_options</>
+   and/or added by <function>sequence_read_state</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_save_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors)
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname)
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a..223403d 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,7 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  tablesample transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc sequence \
+			  spgist tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 86b9ae1..e4f3825 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -888,8 +888,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
- * AM's options parser function in the case of a tuple corresponding to an
- * index, or NULL otherwise.
+ * or sequence AM's options parser function in the case of a tuple corresponding
+ * to an index or sequence respectively, or NULL otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
@@ -921,7 +921,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1374,14 +1375,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for access methods.
  *
- *	amoptions	index AM's option parser function
+ *	amoptions	AM's option parser function
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+am_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 {
 	Assert(amoptions != NULL);
 
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..1784799
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqamapi.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqamapi.c b/src/backend/access/sequence/seqamapi.c
new file mode 100644
index 0000000..bfdd589
--- /dev/null
+++ b/src/backend/access/sequence/seqamapi.c
@@ -0,0 +1,191 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqamapi.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amstate.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_am.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * GetSeqAmRoutine - call the specified access method handler routine
+ * to get its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutine(Oid amhandler)
+{
+	Datum			datum;
+	SeqAmRoutine   *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SeqAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SeqAmRoutine))
+		elog(ERROR, "sequence access method handler function %u did not return an SeqAmRoutine struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * GetSeqAmRoutineByAMId - look up the handler of the sequence access method
+ * for the given OID, and retrieve its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineByAMId(Oid amoid)
+{
+	HeapTuple		tuple;
+	regproc			amhandler;
+	Form_pg_am		amform;
+
+	/* 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);
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("sequence access method \"%s\" does not have a handler",
+						NameStr(amform->amname))));
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function. */
+	return GetSeqAmRoutine(amhandler);
+}
+
+/*
+ * GetSeqAmRoutineForRelation - look up the handler of the sequence access
+ * method for the given sequence Relation.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineForRelation(Relation relation)
+{
+	SeqAmRoutine *seqam;
+	SeqAmRoutine *cseqam;
+
+	if (relation->rd_seqamroutine == NULL)
+	{
+		if (!OidIsValid(relation->rd_rel->relam))
+		{
+			char *relname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(relation)),
+													   RelationGetRelationName(relation));
+			elog(ERROR, "relation %s isn't sequence", relname);
+		}
+
+		seqam = GetSeqAmRoutineByAMId(relation->rd_rel->relam);
+		/* Save the data for later reuse in CacheMemoryContext */
+		cseqam = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												   sizeof(SeqAmRoutine));
+		memcpy(cseqam, seqam, sizeof(SeqAmRoutine));
+		relation->rd_seqamroutine = cseqam;
+	}
+
+	return relation->rd_seqamroutine;
+}
+
+/*
+ * ValidateSeqAmRoutine - to sanity check for the SeqAmRoutine returned by a
+ * handler.
+ *
+ * At the moment we only check that the StateTypeOid is valid type oid and that
+ * the type it points to is fixed width and will fit into the sequence page.
+ */
+void
+ValidateSeqAmRoutine(SeqAmRoutine *routine)
+{
+#define MAX_SEQAM_STATE_LEN \
+	MaxHeapTupleSize - MinHeapTupleSize - MAXALIGN(offsetof(FormData_pg_sequence, amstate))
+
+	Oid	stattypeoid = routine->StateTypeOid;
+
+	if (!get_typisdefined(stattypeoid))
+		ereport(ERROR,
+				(errmsg("type with OID %u does not exist",
+						stattypeoid)));
+
+	if (TypeIsToastable(stattypeoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" must not be toastable",
+						format_type_be(stattypeoid))));
+
+	if (get_typlen(stattypeoid) >= MAX_SEQAM_STATE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" exceeds maximum allowed length (%lu)",
+						format_type_be(stattypeoid), MAX_SEQAM_STATE_LEN)));
+
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..2d31478
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,408 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+#include "utils/typcache.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+static bytea *seqam_local_reloptions(Datum reloptions, bool validate);
+static Datum seqam_local_init(Relation seqrel, Form_pg_sequence seq,
+							  int64 restart_value, bool restart_requested,
+							  bool is_init);
+static int64 seqam_local_alloc(Relation seqrel, SequenceHandle *seqh,
+							   int64 nrequested, int64 *last);
+static void seqam_local_setval(Relation seqrel, SequenceHandle *seqh,
+							   int64 new_value);
+static Datum seqam_local_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+								  Datum amstate);
+
+static char *
+seqam_local_state_get_part(StringInfo buf, char *ptr, bool *found)
+{
+	*found = false;
+
+	resetStringInfo(buf);
+
+	while (*ptr != ',' && *ptr != ')')
+	{
+		char		ch = *ptr++;
+
+		if (ch == '\0')
+		{
+			*found = false;
+			return ptr;
+		}
+
+		appendStringInfoChar(buf, ch);
+
+		if (!*found)
+			*found = true;
+	}
+
+	return ptr;
+}
+
+Datum
+seqam_local_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr;
+	Datum		d;
+	bool		found;
+	StringInfoData		buf;
+	LocalSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	initStringInfo(&buf);
+
+	/* last_value is required */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (!found)
+		goto malformed;
+	d = DirectFunctionCall1(int8in, CStringGetDatum(buf.data));
+	state->last_value = DatumGetInt64(d);
+	if (*ptr == ',')
+		ptr++;
+
+	/* is_called is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(boolin, CStringGetDatum(buf.data));
+		state->is_called = DatumGetBool(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->is_called = true;
+
+	/* log_cnt is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(int4in, CStringGetDatum(buf.data));
+		state->log_cnt = DatumGetInt32(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->log_cnt = 0;
+
+	if (*ptr != ')' || *(++ptr) != '\0')
+		goto malformed;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_local_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_local_state_out(PG_FUNCTION_ARGS)
+{
+	LocalSequenceState *state = (LocalSequenceState *) PG_GETARG_POINTER(0);
+	StringInfoData		buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s,%d)",
+					 state->last_value, state->is_called ? "t" : "f",
+					 state->log_cnt);
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the local sequence access method.
+ */
+Datum
+seqam_local_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine *seqam = makeNode(SeqAmRoutine);
+
+	seqam->StateTypeOid = SEQAMLOCALSTATEOID;
+	seqam->amoptions = seqam_local_reloptions;
+	seqam->Init = seqam_local_init;
+	seqam->Alloc = seqam_local_alloc;
+	seqam->Setval = seqam_local_setval;
+	seqam->GetState = seqam_local_get_state;
+	seqam->SetState = seqam_local_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+static bytea *
+seqam_local_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+static Datum
+seqam_local_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	LocalSequenceState	   *localseq;
+
+	if (is_init)
+		localseq = palloc0(sizeof(LocalSequenceState));
+	else
+		localseq = (LocalSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		localseq->last_value = restart_value;
+		localseq->is_called = false;
+	}
+
+	localseq->log_cnt = 0;
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+static int64
+seqam_local_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+				  int64 *last)
+{
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) seq->amstate;
+
+	next = result = localseq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = localseq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_start_update(seqh, logit);
+
+	if (logit)
+	{
+		localseq->last_value = next;
+		localseq->log_cnt = 0;
+		localseq->is_called = true;
+
+		sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	}
+
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_save_state(seqh, PointerGetDatum(localseq), false);
+
+	sequence_finish_update(seqh);
+
+	return result;
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+static void
+seqam_local_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq;
+
+	amstate = sequence_read_state(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	localseq->last_value = new_value;		/* last fetched number */
+	localseq->is_called = true;
+	localseq->log_cnt = 0;			/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+static Datum
+seqam_local_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq = palloc(sizeof(LocalSequenceState));
+
+	amstate = sequence_read_state(seqh);
+	memcpy(localseq, DatumGetPointer(amstate), sizeof(LocalSequenceState));
+	sequence_release_tuple(seqh);
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+static void
+seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+					  Datum amstate)
+{
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(localseq->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 41d2fd4..e684f60 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -251,6 +251,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 6a4a9d9..47ba31c 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -41,6 +41,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
@@ -88,6 +89,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -849,6 +851,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -922,6 +925,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1035,6 +1039,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1263,6 +1268,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1275,7 +1281,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the pg_am record.
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1330,6 +1337,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = AccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2506344..90a148f 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index f40a005..40b5e91 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -291,6 +291,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index c79cbb1..055f0db 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -16,6 +16,7 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
 #include "access/xact.h"
 #include "catalog/binary_upgrade.h"
 #include "catalog/catalog.h"
@@ -130,6 +131,17 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	 */
 	switch(stmt->amtype)
 	{
+		case AMTYPE_SEQUENCE:
+			{
+				SeqAmRoutine *routine;
+
+				amhandler = lookup_am_handler_func(stmt->handler_name,
+												   SEQ_AM_HANDLEROID);
+
+				routine = GetSeqAmRoutine(amhandler);
+				ValidateSeqAmRoutine(routine);
+			}
+			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -207,6 +219,8 @@ get_am_type_string(char amtype)
 {
 	switch (amtype)
 	{
+		case AMTYPE_SEQUENCE:
+			return "sequence";
 		default:
 			elog(ERROR, "invalid access method type \"%c\"", amtype);
 	}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5cb28cf..54a22da 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -680,6 +680,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index fcb0331..5537d44 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 13b04e6..92ae4db 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -536,7 +536,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..64d7e89 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,16 +14,22 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/tupmacs.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +42,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,24 +82,112 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	Oid			statetyp;
+	int16		statetyplen;
+	bool		statetypbyval;
+	HeapTupleData tup;
+	bool		inupdate;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
  */
 static SeqTableData *last_used_seq = NULL;
 
+static HeapTuple build_seq_tuple(Relation rel, SeqAmRoutine *seqam,
+								 Form_pg_sequence new, int64 restart_value);
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+
+/*
+ * Build template column definition for a sequence relation.
+*/
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_not_null = true;
+	coldef->is_from_type = false;
+	/* Force plain storage. */
+	coldef->storage = 'p';
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(Oid amstateTypeOid)
+{
+	List	   *seqcols;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		switch (colid)
+		{
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+			case SEQ_COL_AMSTATE:
+				coldef->typeName = makeTypeNameFromOid(amstateTypeOid, -1);
+				coldef->colname = "amstate";
+				break;
+		}
+
+		seqcols = lappend(seqcols, coldef);
+	}
 
+	return seqcols;
+}
 
 /*
  * DefineSequence
@@ -111,14 +200,12 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
+	List	   *seqcols;
+	SeqAmRoutine *seqam;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -147,102 +234,33 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
-	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
-
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
-		{
-			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
-				break;
-			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
-				break;
-			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
-				break;
-			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
-				break;
-			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
-				break;
-			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
-			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
-				break;
-		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
-	}
+	if (seq->accessMethod)
+		seqamid = get_am_oid(seq->accessMethod, AMTYPE_SEQUENCE, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
+	seqam = GetSeqAmRoutineByAMId(seqamid);
+	/* Build column definitions. */
+	seqcols = BuildSeqColumnDefList(seqam->StateTypeOid);
 
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
+	stmt->tableElts = seqcols;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	/* Build new sequence tuple and store it. */
+	tuple = build_seq_tuple(rel, seqam, &new, new.start_value);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +272,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +286,89 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
+	TupleDesc			tupDesc;
+	Datum	   values[SEQ_COL_LASTCOL];
+	bool	   nulls[SEQ_COL_LASTCOL];
+	SeqAmRoutine *seqam;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
-	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tupDesc = RelationGetDescr(seqrel);
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(seqrel, seq, seq->start_value,
+											  true, false);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
+}
+
+/*
+ * Build sequence tuple based on the sequence form and fill in the
+ * sequence AM specific info as well.
+ */
+static HeapTuple
+build_seq_tuple(Relation rel, SeqAmRoutine *seqam, Form_pg_sequence new,
+				int64 restart_value)
+{
+	TupleDesc	tupDesc;
+	HeapTuple	tuple;
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+
+	tupDesc = RelationGetDescr(rel);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new->start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new->increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new->max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new->min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new->cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new->is_cycled);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(rel, new, restart_value,
+											  false, true);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	return tuple;
 }
 
 /*
@@ -361,7 +411,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +431,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,6 +439,69 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 }
 
 /*
+ * Replace the type of amstate column.
+ *
+ * We don't do AlterTable here as that produces dead columns which we don't
+ * want. This is safe because the sequence page is controlled by code in this
+ * module and isn't changed the same way as a table.
+ *
+ * TODO: check if anybody is depending on the row-type associated with the
+ * sequence.
+ */
+static void
+replace_sequence_amstate_col(Oid seqrelid, Oid typid)
+{
+	Relation	attr_rel;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	HeapTuple	tp,
+				attr_tuple,
+				newattr_tuple;
+	Form_pg_type	typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+
+	memset(nulls, 0, sizeof(nulls));
+	memset(replace, 0, sizeof(replace));
+
+	replace[Anum_pg_attribute_atttypid - 1] = true;
+	replace[Anum_pg_attribute_attlen - 1] = true;
+	replace[Anum_pg_attribute_attbyval - 1] = true;
+	replace[Anum_pg_attribute_attalign - 1] = true;
+
+	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(typid);
+	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(typtup->typlen);
+	values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(typtup->typbyval);
+	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(typtup->typalign);
+
+	/* Build DROP command for amstate of old AM. */
+	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+	attr_tuple = SearchSysCache2(ATTNUM,
+								 ObjectIdGetDatum(seqrelid),
+								 Int16GetDatum(SEQ_COL_AMSTATE));
+	if (!HeapTupleIsValid(attr_tuple))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 SEQ_COL_AMSTATE, seqrelid);
+
+	newattr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel),
+									  values, nulls, replace);
+	simple_heap_update(attr_rel, &newattr_tuple->t_self, newattr_tuple);
+	CatalogUpdateIndexes(attr_rel, newattr_tuple);
+
+	ReleaseSysCache(tp);
+	heap_freetuple(newattr_tuple);
+	ReleaseSysCache(attr_tuple);
+
+	heap_close(attr_rel, RowExclusiveLock);
+}
+
+/*
  * AlterSequence
  *
  * Modify the definition of a sequence relation
@@ -406,19 +509,24 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence seq,
+					 new;
 	List	   *owned_by;
 	ObjectAddress address;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
+	SeqAmRoutine *oldseqam;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +534,187 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
+	oldamid = seqrel->rd_rel->relam;
+	oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	if (stmt->accessMethod)
+		seqamid = get_am_oid(stmt->accessMethod, AMTYPE_SEQUENCE, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(stmt->options, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		HeapTuple       seqamtup;
+		Form_pg_am		form_am;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
+		SeqAmRoutine	   *newseqam;
+
+		oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = oldseqam->Alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
+
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(AMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
+
+		newseqam = GetSeqAmRoutineByAMId(seqamid);
+
+		form_am = (Form_pg_am) GETSTRUCT(seqamtup);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		validnsps[0] = NameStr(form_am->amname);
+		validnsps[1] = NULL;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		(void) am_reloptions(newseqam->amoptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-		PageSetLSN(page, recptr);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
+
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
+
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * And change the type definition.
+		 *
+		 * We can't use AlterTable internals here because the sequence
+		 * has to have the expected number of columns and no
+		 * attisdropped = true columns.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		replace_sequence_amstate_col(seqrelid, newseqam->StateTypeOid);
+		CommandCounterIncrement();
+
+		/* Rebuild the sequence tuple and save it. */
+		tuple = build_seq_tuple(seqrel, newseqam, new, restart_value);
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										AccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = AccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
+	else
+	{
+		Datum	   newamstate;
 
-	END_CRIT_SECTION();
+		sequence_check_range(restart_value, new->min_value, new->max_value,
+							 restart_requested ? "RESTART" : "START");
 
-	UnlockReleaseBuffer(buf);
+		/* Let the new sequence AM initialize. */
+		newamstate = oldseqam->Init(seqrel, new, restart_value,
+									restart_requested, false);
+
+		sequence_start_update(&seqh, true);
+		memcpy(seq, new, offsetof(FormData_pg_sequence, amstate));
+		sequence_save_state(&seqh, newamstate, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +755,26 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +799,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam->Alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +816,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for syncrep.
-	 * It's sufficient to ensure the toplevel transaction has an xid, no need
-	 * to assign xids subxacts, that'll already trigger an appropriate wait.
-	 * (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +827,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +888,26 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,106 +924,86 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam->Setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	last_used_seq = elm;
 
-	UnlockReleaseBuffer(buf);
+	sequence_close(&seqh);
 
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	LocalSequenceState	state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
+
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-	do_setval(relid, next, true);
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Build the state and pass it to sequence AM. */
+	state.last_value = next;
+	state.log_cnt = 0;
+	state.is_called = iscalled;
+	seqam->SetState(seqh.rel, &seqh, PointerGetDatum(&state));
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-	PG_RETURN_INT64(next);
-}
+	/* Reset local cached data */
+	elm->cached = elm->last;
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1034,11 +1062,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1117,58 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->statetyp = 6025; /* TODO */
+	seqh->statetyplen = -1;
+	seqh->statetypbyval = false;
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(!seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1178,170 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+Form_pg_sequence
+sequence_read_options(SequenceHandle *seqh)
+{
+	return (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
+}
+
+Datum
+sequence_read_state(SequenceHandle *seqh)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+
+	return PointerGetDatum(seq->amstate);
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool dowal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+
+	if (seqh->statetyplen < 0)
+	{
+		get_typlenbyval(seqh->statetyp, &seqh->statetyplen,
+						&seqh->statetypbyval);
+		Assert(seqh->statetyplen > 0);
+	}
+
+	if (dowal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_save_state(SequenceHandle *seqh, Datum amstate, bool dowal)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+	Page				page;
+
+	/*
+	 * Update the state data inline.
+	 *
+	 * This is only needed when the provided amstate datum points to different
+	 * data than what is already in the tuple.
+	 */
+	if (DatumGetPointer(amstate) != seq->amstate)
+	{
+		if (seqh->statetypbyval)
+			store_att_byval(seq->amstate, amstate, seqh->statetyplen);
+		else
+			memmove(seq->amstate, DatumGetPointer(amstate), seqh->statetyplen);
+	}
+
+	page = BufferGetPage(seqh->buf);
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (dowal && RelationNeedsWAL(seqh->rel))
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	Assert(BufferIsValid(seqh->buf));
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1351,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1428,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1436,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1445,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1453,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1460,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1473,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1526,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1540,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1650,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1678,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1686,85 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Datum		state;
+	char	   *statestr;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typoutput;
+	bool		typisvarlena;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type output function. */
+	getTypeOutputInfo(seqam->StateTypeOid, &typoutput, &typisvarlena);
+
+	/* Get the output and convert it to string. */
+	state = seqam->GetState(seqh.rel, &seqh);
+	statestr = OidOutputFunctionCall(typoutput, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_TEXT_P(cstring_to_text(statestr));
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *statestr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typinput,
+				typioparam;
+	Datum		state;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type input function. */
+	getTypeInputInfo(seqam->StateTypeOid, &typinput, &typioparam);
+
+	/* Convert the string to the state type and set it as new state. */
+	state = OidInputFunctionCall(typinput, statestr, typioparam, -1);
+	seqam->SetState(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1830,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 96dc923..6e6ee5d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -269,6 +270,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -454,7 +456,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -473,7 +475,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -551,13 +552,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		SeqAmRoutine *seqam;
+
+		Assert(relamid != InvalidOid);
+		seqam = GetSeqAmRoutineByAMId(relamid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 NULL, true, false);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		(void) am_reloptions(seqam->amoptions, reloptions, true);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -678,6 +695,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3308,7 +3326,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW |
+								ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4308,6 +4327,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4351,8 +4373,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a table, view, or foreign table");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9403,12 +9425,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqamroutine->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
+					 errmsg("\"%s\" is not a table, view, materialized view, index, sequence, or TOAST table",
 							RelationGetRelationName(rel))));
 			break;
 	}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 227d382..c5b73c0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2115,7 +2115,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e9d9ba2..ca0f481 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 85bd5ac..4cf5136 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3561,7 +3561,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3574,7 +3576,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index f19fde1..070ab02 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1626,7 +1626,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1637,7 +1639,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b307b48..78b387c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -263,7 +264,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 		DeallocateStmt PrepareStmt ExecuteStmt
 		DropOwnedStmt ReassignOwnedStmt
 		AlterTSConfigurationStmt AlterTSDictionaryStmt
-		CreateMatViewStmt RefreshMatViewStmt
+		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
 
 %type <node>	select_no_parens select_with_parens select_clause
 				simple_select values_clause
@@ -604,7 +605,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
@@ -789,6 +791,7 @@ stmt :
 			| CommentStmt
 			| ConstraintsSetStmt
 			| CopyStmt
+			| CreateAmStmt
 			| CreateAsStmt
 			| CreateAssertStmt
 			| CreateCastStmt
@@ -823,6 +826,7 @@ stmt :
 			| DeleteStmt
 			| DiscardStmt
 			| DoStmt
+			| DropAmStmt
 			| DropAssertStmt
 			| DropCastStmt
 			| DropFdwStmt
@@ -3585,7 +3589,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3607,7 +3637,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3615,11 +3669,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3678,7 +3755,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
@@ -4708,6 +4785,56 @@ row_security_cmd:
 
 /*****************************************************************************
  *
+ *		QUERY:
+ *             CREATE ACCESS METHOD name TYPE type_name HANDLER handler_name
+ *
+ *****************************************************************************/
+
+CreateAmStmt:
+			CREATE ACCESS METHOD name TYPE_P SEQUENCE HANDLER handler_name
+				{
+					CreateAmStmt *n = makeNode(CreateAmStmt);
+					n->amname = $4;
+					n->handler_name = $8;
+					n->amtype = AMTYPE_SEQUENCE;
+					$$ = (Node *) n;
+				}
+		;
+
+/*****************************************************************************
+ *
+ *		QUERY :
+ *				DROP ACCESS METHOD name
+ *
+ ****************************************************************************/
+
+DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+				{
+					DropStmt *n = makeNode(DropStmt);
+					n->removeType = OBJECT_ACCESS_METHOD;
+					n->objects = list_make1(list_make1(makeString($4)));
+					n->arguments = NIL;
+					n->missing_ok = false;
+					n->behavior = $5;
+					n->concurrent = false;
+					$$ = (Node *) n;
+				}
+				|  DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+				{
+					DropStmt *n = makeNode(DropStmt);
+					n->removeType = OBJECT_ACCESS_METHOD;
+					n->objects = list_make1(list_make1(makeString($6)));
+					n->arguments = NIL;
+					n->missing_ok = true;
+					n->behavior = $7;
+					n->concurrent = false;
+					$$ = (Node *) n;
+				}
+		;
+
+
+/*****************************************************************************
+ *
  *		QUERIES :
  *				CREATE TRIGGER ...
  *				DROP TRIGGER ...
@@ -13778,6 +13905,7 @@ unreserved_keyword:
 			| MATCH
 			| MATERIALIZED
 			| MAXVALUE
+			| METHOD
 			| MINUTE_P
 			| MINVALUE
 			| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8ae5400..b0af9c7 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -29,6 +29,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -456,6 +457,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4d0aac9..8b95577 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -965,7 +965,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -998,7 +999,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index dd447cf..6c1456e 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -399,6 +399,31 @@ index_am_handler_out(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();			/* keep compiler quiet */
 }
 
+/*
+ * seq_am_handler_int		- input routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_in(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * seq_am_handler_out		- output routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_out(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot display a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
 
 /*
  * tsm_handler_in		- input routine for pseudo-type TSM_HANDLER.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 130c06d..b9686da 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -260,6 +261,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -424,6 +426,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	amoptions_function	amoptions = NULL;
 
 	relation->rd_options = NULL;
 
@@ -432,10 +435,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_amroutine->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqamroutine->amoptions;
+			break;
 		default:
 			return;
 	}
@@ -447,8 +455,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_amroutine->amoptions : NULL);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1049,11 +1056,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1555,6 +1565,34 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation relation)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	SeqAmRoutine	   *result, *tmp;
+
+	/*
+	 * Look up the index's access method, save the OID of its handler function
+	 */
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 relation->rd_rel->relam);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amtype == AMTYPE_SEQUENCE);
+	relation->rd_amhandler = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	result = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												 sizeof(SeqAmRoutine));
+	tmp = GetSeqAmRoutine(relation->rd_amhandler);
+	memcpy(result, tmp, sizeof(SeqAmRoutine));
+	relation->rd_seqamroutine = result;
+}
 
 /*
  *		formrdesc
@@ -4885,6 +4923,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqamroutine == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index ea5a09a..32cff2d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqamapi.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 8999bcc..534362f 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -45,6 +45,7 @@
 #include "access/attnum.h"
 #include "access/sysattr.h"
 #include "access/transam.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_cast.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_default_acl.h"
@@ -4120,6 +4121,7 @@ getAccessMethods(Archive *fout, int *numAccessMethods)
 	int			i_tableoid;
 	int			i_oid;
 	int			i_amname;
+	int			i_amtype;
 	int			i_amhandler;
 
 	/* Before 9.6, there are no user-defined access methods */
@@ -4138,7 +4140,7 @@ getAccessMethods(Archive *fout, int *numAccessMethods)
 	 * Select only user-defined access methods assuming all built-in access
 	 * methods have oid < 10000.
 	 */
-	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, "
+	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, "
 					  "amhandler::pg_catalog.regproc AS amhandler "
 					  "FROM pg_am "
 					  "WHERE oid >= 10000");
@@ -4154,6 +4156,7 @@ getAccessMethods(Archive *fout, int *numAccessMethods)
 	i_oid = PQfnumber(res, "oid");
 	i_amname = PQfnumber(res, "amname");
 	i_amhandler = PQfnumber(res, "amhandler");
+	i_amtype = PQfnumber(res, "amtype");
 
 	for (i = 0; i < ntups; i++)
 	{
@@ -4164,6 +4167,7 @@ getAccessMethods(Archive *fout, int *numAccessMethods)
 		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
 		aminfo[i].dobj.namespace = NULL;
 		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+		aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
 
 		/* Decide whether we want to dump it */
 		selectDumpableObject(&(aminfo[i].dobj), dopt);
@@ -4726,6 +4730,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4777,6 +4782,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4819,6 +4825,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4861,6 +4868,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4903,6 +4911,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4943,6 +4952,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4982,6 +4992,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5021,6 +5032,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5060,6 +5072,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5098,6 +5111,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5132,6 +5146,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5161,6 +5176,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5200,6 +5216,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5253,6 +5270,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5318,6 +5336,10 @@ getTables(Archive *fout, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -11538,6 +11560,7 @@ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
 	PQExpBuffer delq;
 	PQExpBuffer labelq;
 	char	   *qamname;
+	cosnt char *amtype;
 
 	/* Skip if not to be dumped */
 	if (!aminfo->dobj.dump || dopt->dataOnly)
@@ -11547,10 +11570,26 @@ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
 	delq = createPQExpBuffer();
 	labelq = createPQExpBuffer();
 
+	switch (aminfo->amtype)
+	{
+		case 'i':
+			amtype = "INDEX";
+			break;
+		case 'S':
+			amtype = "SEQUENCE";
+			break;
+		default:
+			/* shouldn't get here */
+			exit_horribly(NULL,
+					  "unrecognized access method type: %c\n",
+						  aminfo->amtype);
+			amtype = "";			/* keep compiler quiet */
+	}
+
 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
 
-	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s HANDLER %s;\n",
-					  qamname, aminfo->amhandler);
+	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s TYPE %s HANDLER %s;\n",
+					  qamname, amtype, aminfo->amhandler);
 
 	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
 					  qamname);
@@ -15244,7 +15283,8 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -15324,6 +15364,37 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.6 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.amname\n"
+								 "FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -15364,6 +15435,13 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -15430,6 +15508,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15446,16 +15527,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.6 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15468,14 +15562,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 0fb6087..d9a26c6 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -172,6 +172,7 @@ typedef struct _accessMethodInfo
 {
 	DumpableObject dobj;
 	char	   *amhandler;
+	char		amtype;
 } AccessMethodInfo;
 
 typedef struct _opclassInfo
@@ -230,6 +231,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_class) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 3bd5bd8..52d5625 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -1333,7 +1333,8 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
 			return;
 		case DO_ACCESS_METHOD:
 			snprintf(buf, bufsize,
-					 "");
+					 "ACCESS METHOD %s  (ID %d OID %u)",
+					 obj->name, obj->dumpId, obj->catId.oid);
 			return;
 		case DO_OPCLASS:
 			snprintf(buf, bufsize,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index fd8dc91..3ca6ebe 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1403,30 +1403,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1470,13 +1446,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1805,6 +1823,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1814,12 +1834,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.amname\n"
+								"FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 469ac67..69135aa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -273,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
+extern bytea *am_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqamapi.h b/src/include/access/seqamapi.h
new file mode 100644
index 0000000..a4c826a
--- /dev/null
+++ b/src/include/access/seqamapi.h
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqamapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAMAPI_H
+#define SEQAMAPI_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef bytea * (*ParseRelOptions_function) (Datum reloptions,
+											 bool validate);
+
+typedef Datum (*SeqAMInit_function) (Relation seqrel,
+									 Form_pg_sequence seq,
+									 int64 restart_value,
+									 bool restart_requested,
+									 bool is_init);
+
+typedef int64 (*SeqAMAlloc_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 nrequested,
+									  int64 *last);
+
+typedef void (*SeqAMSetval_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 new_value);
+
+typedef Datum (*SeqAMGetState_function) (Relation seqrel,
+										 SequenceHandle *seqh);
+typedef void (*SeqAMSetState_function) (Relation seqrel, SequenceHandle *seqh,
+										Datum amstate);
+
+typedef struct SeqAmRoutine
+{
+	NodeTag		type;
+
+	/* Custom columns needed by the AM */
+	Oid			StateTypeOid;
+
+	/* Function for parsing reloptions */
+	ParseRelOptions_function	amoptions;
+
+	/* Initialization */
+	SeqAMInit_function			Init;
+
+	/* nextval handler */
+	SeqAMAlloc_function			Alloc;
+
+	/* State manipulation functions */
+	SeqAMSetval_function		Setval;
+	SeqAMGetState_function		GetState;
+	SeqAMSetState_function		SetState;
+} SeqAmRoutine;
+
+extern SeqAmRoutine *GetSeqAmRoutine(Oid seqamhandler);
+extern SeqAmRoutine *GetSeqAmRoutineByAMId(Oid amoid);
+extern SeqAmRoutine *GetSeqAmRoutineForRelation(Relation relation);
+extern void ValidateSeqAmRoutine(SeqAmRoutine *routine);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern Form_pg_sequence sequence_read_options(SequenceHandle *seqh);
+extern Datum sequence_read_state(SequenceHandle *seqh);
+extern void sequence_start_update(SequenceHandle *seqh, bool dowal);
+extern void sequence_save_state(SequenceHandle *seqh, Datum seqstate,
+								bool dowal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+/* We need this public for serval3 */
+typedef struct LocalSequenceState
+{
+	int64   last_value;
+	int32	log_cnt;
+	bool	is_called;
+} LocalSequenceState;
+
+extern Datum seqam_local_state_in(PG_FUNCTION_ARGS);
+extern Datum seqam_local_state_out(PG_FUNCTION_ARGS);
+extern Datum seqam_local_handler(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAMAPI_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..d6b3cd7 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index f1bdee4..731abae 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -39,6 +39,7 @@ CATALOG(pg_am,2601)
 	/*----------
 	 * Type of access method. Possible values are
 	 *		'i': Index access method
+	 *		'S': Sequence access method
 	 *----------
 	 */
 	char		amtype;
@@ -61,6 +62,7 @@ typedef FormData_pg_am *Form_pg_am;
 #define Anum_pg_am_amtype				3
 
 #define		AMTYPE_INDEX				'i'		/* index */
+#define		AMTYPE_SEQUENCE				'S'		/* sequence */
 
 /* ----------------
  *		initial contents of pg_am
@@ -86,4 +88,8 @@ DATA(insert OID = 3580 (  brin		brinhandler	i ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
+DATA(insert OID = 6023 (  local		seqam_local_handler	S ));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6023
+
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index aec6c4c..74bb5d0 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1759,6 +1759,10 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v u 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 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 25 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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 s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3683,6 +3687,10 @@ DATA(insert OID = 326  (  index_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f
 DESCR("I/O");
 DATA(insert OID = 327  (  index_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
 DESCR("I/O");
+DATA(insert OID = 6021  (  seq_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 6020 "2275" _null_ _null_ _null_ _null_ _null_ seq_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6022 (  seq_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6020" _null_ _null_ _null_ _null_ _null_ seq_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
 DATA(insert OID = 3311 (  tsm_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 3312 (  tsm_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
@@ -3694,6 +3702,10 @@ DESCR("BERNOULLI tablesample method handler");
 DATA(insert OID = 3314 (  system			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_system_handler _null_ _null_ _null_ ));
 DESCR("SYSTEM tablesample method handler");
 
+/* sequence access method handlers */
+DATA(insert OID = 6024 (  seqam_local_handler	   PGNSP PGUID 12 1 0 0 0 f f f f f f v s 1 0 6020 "2281" _null_ _null_ _null_ _null_ _null_ seqam_local_handler _null_ _null_ _null_ ));
+DESCR("Local SequenceAM handler");
+
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
 DESCR("MD5 hash");
@@ -5225,6 +5237,12 @@ DESCR("row security for current context active on table by table name");
 DATA(insert OID = 3400 ( pg_config PGNSP PGUID 12 1 23 0 0 f f f f t t i r 0 0 2249 "" "{25,25}" "{o,o}" "{name,setting}" _null_ _null_ pg_config _null_ _null_ _null_ ));
 DESCR("pg_config binary as a function");
 
+/* Sequence AM */
+DATA(insert OID = 6027 (  seqam_local_state_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 6025 "2275" _null_ _null_ _null_ _null_ _null_ seqam_local_state_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6028 (  seqam_local_state_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6025" _null_ _null_ _null_ _null_ _null_ seqam_local_state_out _null_ _null_ _null_ ));
+DESCR("I/O");
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2c90b76..40214e3 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -696,11 +696,18 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 #define FDW_HANDLEROID	3115
 DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define INDEX_AM_HANDLEROID	325
+DATA(insert OID = 6020 ( seq_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 seq_am_handler_in seq_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQ_AM_HANDLEROID	6020
 DATA(insert OID = 3310 ( tsm_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define TSM_HANDLEROID	3310
 DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 #define ANYRANGEOID		3831
 
+/* Sequence AM composite types */
+DATA(insert OID = 6025 ( seqam_local_state	PGNSP PGUID 16 f b U f t \054 0 0 6026 seqam_local_state_in seqam_local_state_out - - - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQAMLOCALSTATEOID 6025
+DATA(insert OID = 6026 ( _seqam_local_state	PGNSP PGUID -1 f b A f t \054 0 6025 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 
 /*
  * macros
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..e0f6439 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -19,20 +19,18 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "storage/relfilenode.h"
-
+#include "access/htup_details.h"
 
 typedef struct FormData_pg_sequence
 {
-	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
+	/* amstate follows */
+	char		amstate[FLEXIBLE_ARRAY_MEMBER];
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,19 +39,16 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  * Columns of a sequence relation
  */
 
-#define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_STARTVAL		1
+#define SEQ_COL_INCBY			2
+#define SEQ_COL_MAXVALUE		3
+#define SEQ_COL_MINVALUE		4
+#define SEQ_COL_CACHE			5
+#define SEQ_COL_CYCLE			6
+#define SEQ_COL_AMSTATE			7
 
-#define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_FIRSTCOL		SEQ_COL_STARTVAL
+#define SEQ_COL_LASTCOL			SEQ_COL_AMSTATE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +67,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 7a770f4..1bceec8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 7d46763..bf43f3d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -461,6 +461,7 @@ typedef enum NodeTag
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
+	T_SeqAmRoutine,				/* in access/seqamapi.h */
 	T_TsmRoutine				/* in access/tsmapi.h */
 } NodeTag;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8b958b4..859fbad 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2205,8 +2205,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2214,8 +2216,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 6e1e820..7de3404 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -239,6 +239,7 @@ PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
 PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
 PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
 PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
 PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
 PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7ec93c9..d5c50aa 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -582,6 +582,8 @@ extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
 extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_in(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_out(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
 extern Datum internal_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f2bebf2..4fe7e52 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -106,13 +106,18 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	Oid			rd_amhandler;	/* OID of access method's handler function */
+
+	/* These are non-NULL only for a sequence relation */
+	struct SeqAmRoutine *rd_seqamroutine;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
@@ -126,7 +131,6 @@ typedef struct RelationData
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
-	Oid			rd_amhandler;	/* OID of index AM's handler function */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
 	/* use "struct" here to avoid needing to include amapi.h: */
 	struct IndexAmRoutine *rd_amroutine;		/* index AM's API struct */
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644
index 0000000..9242982
--- /dev/null
+++ b/src/test/regress/expected/create_am.out
@@ -0,0 +1,127 @@
+--
+-- Create access method tests
+--
+-- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+-- Drop old index on fast_emp4000
+DROP INDEX grect2ind;
+-- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ERROR:  data type box has no default operator class for access method "gist2"
+HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+-- Make operator class for boxes using gist2
+CREATE OPERATOR CLASS box_ops DEFAULT
+	FOR TYPE box USING gist2 AS
+	OPERATOR 1	<<,
+	OPERATOR 2	&<,
+	OPERATOR 3	&&,
+	OPERATOR 4	&>,
+	OPERATOR 5	>>,
+	OPERATOR 6	~=,
+	OPERATOR 7	@>,
+	OPERATOR 8	<@,
+	OPERATOR 9	&<|,
+	OPERATOR 10	<<|,
+	OPERATOR 11	|>>,
+	OPERATOR 12	|&>,
+	OPERATOR 13	~,
+	OPERATOR 14	@,
+	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+	FUNCTION 2	gist_box_union(internal, internal),
+	FUNCTION 3	gist_box_compress(internal),
+	FUNCTION 4	gist_box_decompress(internal),
+	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+	FUNCTION 6	gist_box_picksplit(internal, internal),
+	FUNCTION 7	gist_box_same(box, box, internal),
+	FUNCTION 9	gist_box_fetch(internal);
+-- Create gist2 index on fast_emp4000
+CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+-- Now check the results from plain indexscan
+SET enable_seqscan = OFF;
+SET enable_indexscan = ON;
+SET enable_bitmapscan = OFF;
+EXPLAIN (COSTS OFF)
+SELECT * FROM fast_emp4000
+    WHERE home_base @ '(200,200),(2000,1000)'::box
+    ORDER BY (home_base[0])[0];
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Sort
+   Sort Key: ((home_base[0])[0])
+   ->  Index Only Scan using grect2ind on fast_emp4000
+         Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
+(4 rows)
+
+SELECT * FROM fast_emp4000
+    WHERE home_base @ '(200,200),(2000,1000)'::box
+    ORDER BY (home_base[0])[0];
+       home_base       
+-----------------------
+ (337,455),(240,359)
+ (1444,403),(1346,344)
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Index Only Scan using grect2ind on fast_emp4000
+         Index Cond: (home_base && '(1000,1000),(0,0)'::box)
+(3 rows)
+
+SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ count 
+-------
+     2
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+                      QUERY PLAN                       
+-------------------------------------------------------
+ Aggregate
+   ->  Index Only Scan using grect2ind on fast_emp4000
+         Index Cond: (home_base IS NULL)
+(3 rows)
+
+SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ count 
+-------
+   278
+(1 row)
+
+-- Try to drop access method: fail because of depending objects
+DROP ACCESS METHOD gist2;
+ERROR:  cannot drop access method gist2 because other objects depend on it
+DETAIL:  operator class box_ops for access method gist2 depends on access method gist2
+index grect2ind depends on operator class box_ops for access method gist2
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- Drop access method cascade
+DROP ACCESS METHOD gist2 CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to operator class box_ops for access method gist2
+drop cascades to index grect2ind
+-- Reset optimizer options
+RESET enable_seqscan;
+RESET enable_indexscan;
+RESET enable_bitmapscan;
+-- Checks for sequence access methods
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+ERROR:  cannot drop access method local2 because other objects depend on it
+DETAIL:  sequence test_seqam depends on access method local2
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ae50c94..f1b1fc2 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -129,8 +129,9 @@ NOTICE:  view "v12_temp" will be a temporary view
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 NOTICE:  view "v13_temp" will be a temporary view
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 7c09fa3..ab0d299 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1586,7 +1586,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
  oid | amname | oid | proname 
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3a19cde 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate 
+-------------+--------------+---------------------+-----------+-------------+-----------+---------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (1,f,0)
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate  
+-------------+--------------+---------------------+-----------+-------------+-----------+----------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (2,t,31)
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c5dfbb5..a30582e 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,52 @@ SELECT table_name, column_name, is_updatable
   FROM information_schema.columns
  WHERE table_name LIKE E'r_\\_view%'
  ORDER BY table_name, ordinal_position;
- table_name |  column_name  | is_updatable 
-------------+---------------+--------------
- ro_view1   | a             | NO
- ro_view1   | b             | NO
- ro_view10  | a             | NO
- ro_view11  | a             | NO
- ro_view11  | b             | NO
- ro_view12  | a             | NO
- ro_view13  | a             | NO
- ro_view13  | b             | NO
- ro_view17  | a             | NO
- ro_view17  | b             | NO
- ro_view18  | a             | NO
- ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
- ro_view19  | start_value   | NO
- ro_view19  | increment_by  | NO
- ro_view19  | max_value     | NO
- ro_view19  | min_value     | NO
- ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
- ro_view19  | is_cycled     | NO
- ro_view19  | is_called     | NO
- ro_view2   | a             | NO
- ro_view2   | b             | NO
- ro_view20  | a             | NO
- ro_view20  | b             | NO
- ro_view20  | g             | NO
- ro_view3   | ?column?      | NO
- ro_view4   | count         | NO
- ro_view5   | a             | NO
- ro_view5   | rank          | NO
- ro_view6   | a             | NO
- ro_view6   | b             | NO
- ro_view7   | a             | NO
- ro_view7   | b             | NO
- ro_view8   | a             | NO
- ro_view8   | b             | NO
- ro_view9   | a             | NO
- ro_view9   | b             | NO
- rw_view14  | ctid          | NO
- rw_view14  | a             | YES
- rw_view14  | b             | YES
- rw_view15  | a             | YES
- rw_view15  | upper         | NO
- rw_view16  | a             | YES
- rw_view16  | b             | YES
- rw_view16  | aa            | YES
-(46 rows)
+ table_name | column_name  | is_updatable 
+------------+--------------+--------------
+ ro_view1   | a            | NO
+ ro_view1   | b            | NO
+ ro_view10  | a            | NO
+ ro_view11  | a            | NO
+ ro_view11  | b            | NO
+ ro_view12  | a            | NO
+ ro_view13  | a            | NO
+ ro_view13  | b            | NO
+ ro_view17  | a            | NO
+ ro_view17  | b            | NO
+ ro_view18  | a            | NO
+ ro_view19  | start_value  | NO
+ ro_view19  | increment_by | NO
+ ro_view19  | max_value    | NO
+ ro_view19  | min_value    | NO
+ ro_view19  | cache_value  | NO
+ ro_view19  | is_cycled    | NO
+ ro_view19  | amstate      | NO
+ ro_view2   | a            | NO
+ ro_view2   | b            | NO
+ ro_view20  | a            | NO
+ ro_view20  | b            | NO
+ ro_view20  | g            | NO
+ ro_view3   | ?column?     | NO
+ ro_view4   | count        | NO
+ ro_view5   | a            | NO
+ ro_view5   | rank         | NO
+ ro_view6   | a            | NO
+ ro_view6   | b            | NO
+ ro_view7   | a            | NO
+ ro_view7   | b            | NO
+ ro_view8   | a            | NO
+ ro_view8   | b            | NO
+ ro_view9   | a            | NO
+ ro_view9   | b            | NO
+ rw_view14  | ctid         | NO
+ rw_view14  | a            | YES
+ rw_view14  | b            | YES
+ rw_view15  | a            | YES
+ rw_view15  | upper        | NO
+ rw_view16  | a            | YES
+ rw_view16  | b            | YES
+ rw_view16  | aa           | YES
+(43 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644
index 0000000..e811ea3
--- /dev/null
+++ b/src/test/regress/sql/create_am.sql
@@ -0,0 +1,89 @@
+--
+-- Create access method tests
+--
+
+-- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+
+-- Drop old index on fast_emp4000
+DROP INDEX grect2ind;
+
+-- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+
+-- Make operator class for boxes using gist2
+CREATE OPERATOR CLASS box_ops DEFAULT
+	FOR TYPE box USING gist2 AS
+	OPERATOR 1	<<,
+	OPERATOR 2	&<,
+	OPERATOR 3	&&,
+	OPERATOR 4	&>,
+	OPERATOR 5	>>,
+	OPERATOR 6	~=,
+	OPERATOR 7	@>,
+	OPERATOR 8	<@,
+	OPERATOR 9	&<|,
+	OPERATOR 10	<<|,
+	OPERATOR 11	|>>,
+	OPERATOR 12	|&>,
+	OPERATOR 13	~,
+	OPERATOR 14	@,
+	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+	FUNCTION 2	gist_box_union(internal, internal),
+	FUNCTION 3	gist_box_compress(internal),
+	FUNCTION 4	gist_box_decompress(internal),
+	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+	FUNCTION 6	gist_box_picksplit(internal, internal),
+	FUNCTION 7	gist_box_same(box, box, internal),
+	FUNCTION 9	gist_box_fetch(internal);
+
+-- Create gist2 index on fast_emp4000
+CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+
+-- Now check the results from plain indexscan
+SET enable_seqscan = OFF;
+SET enable_indexscan = ON;
+SET enable_bitmapscan = OFF;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM fast_emp4000
+    WHERE home_base @ '(200,200),(2000,1000)'::box
+    ORDER BY (home_base[0])[0];
+SELECT * FROM fast_emp4000
+    WHERE home_base @ '(200,200),(2000,1000)'::box
+    ORDER BY (home_base[0])[0];
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+
+-- Try to drop access method: fail because of depending objects
+DROP ACCESS METHOD gist2;
+
+-- Drop access method cascade
+DROP ACCESS METHOD gist2 CASCADE;
+
+-- Reset optimizer options
+RESET enable_seqscan;
+RESET enable_indexscan;
+RESET enable_bitmapscan;
+
+-- Checks for sequence access methods
+
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 58d361d..e967fff 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -131,8 +131,9 @@ CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 6c9784a..6c44d70 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1055,7 +1055,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
 
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..c2b1164 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
-- 
1.9.1

0003-gapless-seq-2015-03-05.patchapplication/x-patch; name=0003-gapless-seq-2015-03-05.patchDownload
From 01e95f9c2d0973ca6681284cd78c296ff6a2d4c9 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Fri, 4 Mar 2016 15:03:44 +0100
Subject: [PATCH 3/3] gapless seq

---
 contrib/Makefile                             |   1 +
 contrib/gapless_seq/Makefile                 |  63 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  57 +++
 contrib/gapless_seq/gapless_seq.c            | 531 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 +++
 doc/src/sgml/contrib.sgml                    |   1 +
 doc/src/sgml/filelist.sgml                   |   1 +
 doc/src/sgml/gapless-seq.sgml                |  24 ++
 12 files changed, 950 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql
 create mode 100644 doc/src/sgml/gapless-seq.sgml

diff --git a/contrib/Makefile b/contrib/Makefile
index bd251f6..b60e4b9 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
 		earthdistance	\
 		file_fdw	\
 		fuzzystrmatch	\
+		gapless_seq	\
 		hstore		\
 		intagg		\
 		intarray	\
diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..130104a
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+ERROR:  cannot drop access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..3b41f86
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,57 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_handler(internal)
+RETURNS SEQ_AM_HANDLER
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_in(cstring)
+RETURNS seqam_gapless_state
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_out(seqam_gapless_state)
+RETURNS cstring
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TYPE seqam_gapless_state (
+	INPUT   = seqam_gapless_state_in,
+	OUTPUT  = seqam_gapless_state_out,
+	INTERNALLENGTH = 16
+);
+COMMENT ON TYPE seqam_gapless_state IS 'state for gapless sequence am';
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	last_value bigint NOT NULL,
+	is_called bool NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relkind = 'S' AND relam = (
+			SELECT oid FROM pg_am WHERE amname = 'gapless' AND amtype = 'S'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD gapless TYPE SEQUENCE HANDLER seqam_gapless_handler;
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..57f1570
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,531 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager / Gapless sequence implementation
+ *
+ *------------------------------------------------------------
+ */
+
+typedef struct GaplessSequenceState
+{
+	int64   last_value;
+	uint32	xid;
+	bool	is_called;
+} GaplessSequenceState;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	int64	last_value;
+	bool	is_called;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static bytea *seqam_gapless_reloptions(Datum reloptions, bool validate);
+static Datum seqam_gapless_init(Relation seqrel, Form_pg_sequence seq,
+								int64 restart_value, bool restart_requested,
+								bool is_init);
+static int64 seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh,
+								 int64 nrequested, int64 *last);
+static void seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh,
+								 int64 new_value);
+static Datum seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+									Datum amstate);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_handler);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_in);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_out);
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+							   int64 last_value, bool is_called);
+
+Datum
+seqam_gapless_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr, *end;
+	Datum		d;
+	GaplessSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	end = ptr;
+	while (*end != ',' && *end != ')')
+	{
+		char		ch = *end++;
+
+		if (ch == '\0')
+			goto malformed;
+	}
+
+	d = DirectFunctionCall1(int8in,
+							CStringGetDatum(pnstrdup(ptr, end-ptr)));
+	state->last_value = DatumGetInt64(d);
+
+	/* is_called is optional */
+	if (*end == ',')
+	{
+		ptr = ++end;
+
+		end = state_str+strlen(state_str)-1;
+
+		if (*end != ')')
+			goto malformed;
+
+		d = DirectFunctionCall1(boolin,
+								CStringGetDatum(pnstrdup(ptr, end-ptr)));
+		state->is_called = DatumGetBool(d);
+	}
+	else
+		state->is_called = true;
+
+	state->xid = InvalidTransactionId;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_gapless_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_gapless_state_out(PG_FUNCTION_ARGS)
+{
+	GaplessSequenceState   *state;
+	StringInfoData			buf;
+
+	state = (GaplessSequenceState *) PG_GETARG_POINTER(0);
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s)",
+					 state->last_value, state->is_called ? "t" : "f");
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the gapless sequence access method.
+ */
+Datum
+seqam_gapless_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine   *seqam = makeNode(SeqAmRoutine);
+	Oid				nspid,
+					typid;
+
+	nspid = get_namespace_oid("gapless_seq", false);
+	typid = GetSysCacheOid2(TYPENAMENSP,
+							PointerGetDatum("seqam_gapless_state"),
+							ObjectIdGetDatum(nspid));
+
+	if (!OidIsValid(typid))
+		elog(ERROR, "cache lookup failed for type \"seqam_gapless_state\"");
+
+	seqam->StateTypeOid = typid;
+	seqam->amoptions = seqam_gapless_reloptions;
+	seqam->Init = seqam_gapless_init;
+	seqam->Alloc = seqam_gapless_alloc;
+	seqam->Setval = seqam_gapless_setval;
+	seqam->GetState = seqam_gapless_get_state;
+	seqam->SetState = seqam_gapless_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+static bytea *
+seqam_gapless_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+static Datum
+seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	Oid			seqrelid = seqrel->rd_id;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	GaplessSequenceState   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(GaplessSequenceState));
+	else
+		seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->xid = GetTopTransactionId();
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, valtuple, seqstate->last_value,
+						   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+static int64
+seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+					int64 *last)
+{
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	Form_pg_sequence		seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, result, true);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* We always WAL log for gapless sequence. */
+	sequence_start_update(seqh, true);
+	seqstate->last_value = result;
+	seqstate->is_called = true;
+	if (seqstate->xid != local_xid)
+		seqstate->xid = local_xid;
+	sequence_save_state(seqh, PointerGetDatum(seqstate), true);
+	sequence_finish_update(seqh);
+
+	*last = result;
+
+	return result;
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+static void
+seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+static Datum
+seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	GaplessSequenceState   *seqstate = palloc0(sizeof(GaplessSequenceState));
+
+	/*
+	 * Get the last value from the values table, if not found use the values
+	 * from sequence typle.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		seqstate->last_value = v->last_value;
+		seqstate->is_called = v->is_called;
+	}
+	else
+	{
+		Datum		amstate = sequence_read_state(seqh);
+
+		memcpy(seqstate, DatumGetPointer(amstate),
+			   sizeof(GaplessSequenceState));
+		sequence_release_tuple(seqh);
+	}
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+static void
+seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+						Datum amstate)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	FormData_pg_sequence   *seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(seqstate->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, seqstate->last_value,
+					   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormData_pg_sequence   *seq = sequence_read_options(seqh);
+	GaplessSequenceState   *seqstate;
+
+	seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (seqstate->xid != local_xid &&
+		   TransactionIdIsInProgress(seqstate->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seqstate->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = sequence_read_options(seqh);
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+				   int64 last_value, bool is_called)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..b49cec6
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index 1b3d2d9..d78feb5 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -116,6 +116,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
  &earthdistance;
  &file-fdw;
  &fuzzystrmatch;
+ &gapless-seq;
  &hstore;
  &intagg;
  &intarray;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 7b10c67..95b8b84 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -119,6 +119,7 @@
 <!ENTITY earthdistance   SYSTEM "earthdistance.sgml">
 <!ENTITY file-fdw        SYSTEM "file-fdw.sgml">
 <!ENTITY fuzzystrmatch   SYSTEM "fuzzystrmatch.sgml">
+<!ENTITY gapless-seq     SYSTEM "gapless-seq.sgml">
 <!ENTITY hstore          SYSTEM "hstore.sgml">
 <!ENTITY intagg          SYSTEM "intagg.sgml">
 <!ENTITY intarray        SYSTEM "intarray.sgml">
diff --git a/doc/src/sgml/gapless-seq.sgml b/doc/src/sgml/gapless-seq.sgml
new file mode 100644
index 0000000..44cc224
--- /dev/null
+++ b/doc/src/sgml/gapless-seq.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless-seq" xreflabel="gapless_seq">
+ <title>gapless_seq</title>
+
+ <indexterm zone="gapless-seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence and hold
+  lock on the sequence itself, so any concurrent nextval() calls have to wait
+  for the transaction to finish. This could be very costly and is not
+  recommended for general usage except in specific applications that require
+  this feature.
+ </para>
+
+</sect1>
-- 
1.9.1

#106David Steele
david@pgmasters.net
In reply to: Petr Jelinek (#105)
Re: Sequence Access Method WIP

On 3/4/16 11:09 PM, Petr Jelinek wrote:

But first here is updated patch for sequence access methods. I went with
the previously discussed custom type as this gives us proper control
over the width of the state and making sure that it's not gonna be
toastable, etc and we need this as sequence is limited to single page.

Attached are 3 separate files. The first one (0001-create-am) is mainly
separate for the reason that there is some overlap with Alexander's
index access methods patch, so I extracted common code into separate
patch as it will make it easier to merge in case we are lucky enough to
get these patches in (but it's still based on Alexander's code). It
provides infra for defining new access methods from SQL, although
without the parser and without any actual access methods.

The 0002-seqam provides the SQL interface and support for sequence
access methods on top of the infra patch, and also provides all the
sequence access methods infrastructure and abstraction and documentation.

And finally the 0003-gapless-seq is example contrib module that
implements dependably and transitionally safe gapless sequence access
method. It's obviously slow as it has to do locking and basically
serialize all the changes to sequence so only one transaction may use it
at a time but it's truly gapless. It also serves as an example of use of
the api and as a test.

As far as I can see Petr has addressed all the outstanding issues in
this patch and it's ready for a review. The patch applies with some
easily-fixed conflicts:

$ git apply -3 ../other/0002-seqam-2015-03-05.patch
error: patch failed: src/include/catalog/pg_proc.h:5225
Falling back to three-way merge...
Applied patch to 'src/include/catalog/pg_proc.h' with conflicts.

Simon, you are signed up to review. Do you have an idea of when you'll
be doing that?

--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#107Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Petr Jelinek (#105)
Re: Sequence Access Method WIP

Petr Jelinek wrote:

And finally the 0003-gapless-seq is example contrib module that implements
dependably and transitionally safe gapless sequence access method. It's
obviously slow as it has to do locking and basically serialize all the
changes to sequence so only one transaction may use it at a time but it's
truly gapless. It also serves as an example of use of the api and as a test.

I'm trying to figure out this one, and I think I found something very
surprising. This code contains this

+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"

which as far as I understand is something like a side table where values
for all the sequences are stored. Is that right? If it is, I admit I
didn't realize that these sequences worked in this way. This seems
broken to me. I had been under the impression that the values were
always stored in the sequence's relation. Maybe I'm misunderstanding;
please explain how this works.

FWIW I find it annoying that this submission's 0001 patch and
Alexander's 0001 have so much in common, yet they aren't similar enough
to consider that either is the definite form. Also, you have the SGML
docs for CREATE ACCESS METHOD in 0002 instead of 0001, so comparing both
versions isn't trivial.

Anyway I'm back at reviewing Alexander's 0001, which should be okay as a
basis for this series regardless of any edits I suggest there.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#108Petr Jelinek
petr@2ndquadrant.com
In reply to: Alvaro Herrera (#107)
Re: Sequence Access Method WIP

On 19/03/16 23:02, Alvaro Herrera wrote:

Petr Jelinek wrote:

And finally the 0003-gapless-seq is example contrib module that implements
dependably and transitionally safe gapless sequence access method. It's
obviously slow as it has to do locking and basically serialize all the
changes to sequence so only one transaction may use it at a time but it's
truly gapless. It also serves as an example of use of the api and as a test.

I'm trying to figure out this one, and I think I found something very
surprising. This code contains this

+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"

which as far as I understand is something like a side table where values
for all the sequences are stored. Is that right? If it is, I admit I
didn't realize that these sequences worked in this way. This seems
broken to me. I had been under the impression that the values were
always stored in the sequence's relation. Maybe I'm misunderstanding;
please explain how this works.

Yes, I did explain in the original submission that added gapless
sequence, which is about million messages upthread so it's
understandable that it's hard to follow.

The sequence am API is non-transactional (and does not use MVCC) as
sequences are non-trasactional and this provides significant performance
boost. But the gapless sequence needs to handle things like
subtransactions and rollbacks so it needs MVCC, for this reasons it also
uses external table to handle that, the locking and stuff is still
handled using normal sequence api but the MVCC part is handled by side
table yes.

Just as a note to you and anybody who wasn't following in the beginning
of this quite long thread - one of the goals of the sequence am api was
to abstract the actual storage and WAL logging of the sequences away
from the extension, so there is no generic WAL or anything like the
index am provides here. At this point though, it might be worth
reevaluating that approach if the generic WAL gets in.

FWIW I find it annoying that this submission's 0001 patch and
Alexander's 0001 have so much in common, yet they aren't similar enough
to consider that either is the definite form. Also, you have the SGML
docs for CREATE ACCESS METHOD in 0002 instead of 0001, so comparing both
versions isn't trivial.

Well it's relatively annoying for the patch author as well. I tried to
write it so that it's as easy to merge as possible - the common code is
in my 0001, except for the SQL (gram.y, docs) because the SQL does not
have any meaning until either indexam or seqeunceam gets in (which also
makes it impossible to submit as separate infrastructure patch, because
there is no point of having the code in without the stuff that either
indexam or sequenceam provides on top of it).

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#109Petr Jelinek
petr@2ndquadrant.com
In reply to: Petr Jelinek (#108)
2 attachment(s)
Re: Sequence Access Method WIP

Hi,

I rebased this on top of the recently committed CREATE ACCESS METHOD.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0002-gapless-seq2016-02-24.patchtext/plain; charset=UTF-8; name=0002-gapless-seq2016-02-24.patchDownload
From c08aac9acb13025d70c14f4d8b185ce990db28dc Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Fri, 4 Mar 2016 15:03:44 +0100
Subject: [PATCH 2/2] gapless seq

---
 contrib/Makefile                             |   1 +
 contrib/gapless_seq/Makefile                 |  63 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  57 +++
 contrib/gapless_seq/gapless_seq.c            | 531 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 +++
 doc/src/sgml/contrib.sgml                    |   1 +
 doc/src/sgml/filelist.sgml                   |   1 +
 doc/src/sgml/gapless-seq.sgml                |  24 ++
 12 files changed, 950 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql
 create mode 100644 doc/src/sgml/gapless-seq.sgml

diff --git a/contrib/Makefile b/contrib/Makefile
index d12dd63..d2a0620 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
 		earthdistance	\
 		file_fdw	\
 		fuzzystrmatch	\
+		gapless_seq	\
 		hstore		\
 		intagg		\
 		intarray	\
diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..130104a
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+ERROR:  cannot drop access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..3b41f86
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,57 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_handler(internal)
+RETURNS SEQ_AM_HANDLER
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_in(cstring)
+RETURNS seqam_gapless_state
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_out(seqam_gapless_state)
+RETURNS cstring
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TYPE seqam_gapless_state (
+	INPUT   = seqam_gapless_state_in,
+	OUTPUT  = seqam_gapless_state_out,
+	INTERNALLENGTH = 16
+);
+COMMENT ON TYPE seqam_gapless_state IS 'state for gapless sequence am';
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	last_value bigint NOT NULL,
+	is_called bool NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relkind = 'S' AND relam = (
+			SELECT oid FROM pg_am WHERE amname = 'gapless' AND amtype = 'S'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD gapless TYPE SEQUENCE HANDLER seqam_gapless_handler;
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..57f1570
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,531 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager / Gapless sequence implementation
+ *
+ *------------------------------------------------------------
+ */
+
+typedef struct GaplessSequenceState
+{
+	int64   last_value;
+	uint32	xid;
+	bool	is_called;
+} GaplessSequenceState;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	int64	last_value;
+	bool	is_called;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static bytea *seqam_gapless_reloptions(Datum reloptions, bool validate);
+static Datum seqam_gapless_init(Relation seqrel, Form_pg_sequence seq,
+								int64 restart_value, bool restart_requested,
+								bool is_init);
+static int64 seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh,
+								 int64 nrequested, int64 *last);
+static void seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh,
+								 int64 new_value);
+static Datum seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+									Datum amstate);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_handler);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_in);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_out);
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+							   int64 last_value, bool is_called);
+
+Datum
+seqam_gapless_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr, *end;
+	Datum		d;
+	GaplessSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	end = ptr;
+	while (*end != ',' && *end != ')')
+	{
+		char		ch = *end++;
+
+		if (ch == '\0')
+			goto malformed;
+	}
+
+	d = DirectFunctionCall1(int8in,
+							CStringGetDatum(pnstrdup(ptr, end-ptr)));
+	state->last_value = DatumGetInt64(d);
+
+	/* is_called is optional */
+	if (*end == ',')
+	{
+		ptr = ++end;
+
+		end = state_str+strlen(state_str)-1;
+
+		if (*end != ')')
+			goto malformed;
+
+		d = DirectFunctionCall1(boolin,
+								CStringGetDatum(pnstrdup(ptr, end-ptr)));
+		state->is_called = DatumGetBool(d);
+	}
+	else
+		state->is_called = true;
+
+	state->xid = InvalidTransactionId;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_gapless_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_gapless_state_out(PG_FUNCTION_ARGS)
+{
+	GaplessSequenceState   *state;
+	StringInfoData			buf;
+
+	state = (GaplessSequenceState *) PG_GETARG_POINTER(0);
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s)",
+					 state->last_value, state->is_called ? "t" : "f");
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the gapless sequence access method.
+ */
+Datum
+seqam_gapless_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine   *seqam = makeNode(SeqAmRoutine);
+	Oid				nspid,
+					typid;
+
+	nspid = get_namespace_oid("gapless_seq", false);
+	typid = GetSysCacheOid2(TYPENAMENSP,
+							PointerGetDatum("seqam_gapless_state"),
+							ObjectIdGetDatum(nspid));
+
+	if (!OidIsValid(typid))
+		elog(ERROR, "cache lookup failed for type \"seqam_gapless_state\"");
+
+	seqam->StateTypeOid = typid;
+	seqam->amoptions = seqam_gapless_reloptions;
+	seqam->Init = seqam_gapless_init;
+	seqam->Alloc = seqam_gapless_alloc;
+	seqam->Setval = seqam_gapless_setval;
+	seqam->GetState = seqam_gapless_get_state;
+	seqam->SetState = seqam_gapless_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+static bytea *
+seqam_gapless_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+static Datum
+seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	Oid			seqrelid = seqrel->rd_id;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	GaplessSequenceState   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(GaplessSequenceState));
+	else
+		seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->xid = GetTopTransactionId();
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, valtuple, seqstate->last_value,
+						   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+static int64
+seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+					int64 *last)
+{
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	Form_pg_sequence		seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, result, true);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* We always WAL log for gapless sequence. */
+	sequence_start_update(seqh, true);
+	seqstate->last_value = result;
+	seqstate->is_called = true;
+	if (seqstate->xid != local_xid)
+		seqstate->xid = local_xid;
+	sequence_save_state(seqh, PointerGetDatum(seqstate), true);
+	sequence_finish_update(seqh);
+
+	*last = result;
+
+	return result;
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+static void
+seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+static Datum
+seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	GaplessSequenceState   *seqstate = palloc0(sizeof(GaplessSequenceState));
+
+	/*
+	 * Get the last value from the values table, if not found use the values
+	 * from sequence typle.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		seqstate->last_value = v->last_value;
+		seqstate->is_called = v->is_called;
+	}
+	else
+	{
+		Datum		amstate = sequence_read_state(seqh);
+
+		memcpy(seqstate, DatumGetPointer(amstate),
+			   sizeof(GaplessSequenceState));
+		sequence_release_tuple(seqh);
+	}
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+static void
+seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+						Datum amstate)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	FormData_pg_sequence   *seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(seqstate->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, seqstate->last_value,
+					   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormData_pg_sequence   *seq = sequence_read_options(seqh);
+	GaplessSequenceState   *seqstate;
+
+	seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (seqstate->xid != local_xid &&
+		   TransactionIdIsInProgress(seqstate->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seqstate->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = sequence_read_options(seqh);
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+				   int64 last_value, bool is_called)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..b49cec6
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index 4e3f337..9852933 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -116,6 +116,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
  &earthdistance;
  &file-fdw;
  &fuzzystrmatch;
+ &gapless-seq;
  &hstore;
  &intagg;
  &intarray;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index fd355dc..2681437 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -119,6 +119,7 @@
 <!ENTITY earthdistance   SYSTEM "earthdistance.sgml">
 <!ENTITY file-fdw        SYSTEM "file-fdw.sgml">
 <!ENTITY fuzzystrmatch   SYSTEM "fuzzystrmatch.sgml">
+<!ENTITY gapless-seq     SYSTEM "gapless-seq.sgml">
 <!ENTITY hstore          SYSTEM "hstore.sgml">
 <!ENTITY intagg          SYSTEM "intagg.sgml">
 <!ENTITY intarray        SYSTEM "intarray.sgml">
diff --git a/doc/src/sgml/gapless-seq.sgml b/doc/src/sgml/gapless-seq.sgml
new file mode 100644
index 0000000..44cc224
--- /dev/null
+++ b/doc/src/sgml/gapless-seq.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless-seq" xreflabel="gapless_seq">
+ <title>gapless_seq</title>
+
+ <indexterm zone="gapless-seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence and hold
+  lock on the sequence itself, so any concurrent nextval() calls have to wait
+  for the transaction to finish. This could be very costly and is not
+  recommended for general usage except in specific applications that require
+  this feature.
+ </para>
+
+</sect1>
-- 
1.9.1

0001-seqam-2016-03-24.patchtext/plain; charset=UTF-8; name=0001-seqam-2016-03-24.patchDownload
From 09ed03975d8345c052387866cafef47ad2bc060b Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Wed, 2 Mar 2016 16:15:33 +0100
Subject: [PATCH 1/2] seqam

---
 doc/src/sgml/filelist.sgml                    |    1 +
 doc/src/sgml/postgres.sgml                    |    1 +
 doc/src/sgml/ref/alter_sequence.sgml          |   55 +
 doc/src/sgml/ref/create_access_method.sgml    |    9 +-
 doc/src/sgml/ref/create_sequence.sgml         |   25 +
 doc/src/sgml/seqam.sgml                       |  312 +++++
 src/backend/access/Makefile                   |    4 +-
 src/backend/access/common/reloptions.c        |   13 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqamapi.c        |  191 ++++
 src/backend/access/sequence/seqlocal.c        |  408 +++++++
 src/backend/bootstrap/bootparse.y             |    1 +
 src/backend/catalog/heap.c                    |   17 +-
 src/backend/catalog/objectaddress.c           |    1 +
 src/backend/catalog/toasting.c                |    1 +
 src/backend/commands/amcmds.c                 |   38 +-
 src/backend/commands/cluster.c                |    1 +
 src/backend/commands/createas.c               |    3 +-
 src/backend/commands/indexcmds.c              |    2 +-
 src/backend/commands/sequence.c               | 1502 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |   49 +-
 src/backend/commands/typecmds.c               |    3 +-
 src/backend/commands/view.c                   |    3 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   91 +-
 src/backend/parser/parse_utilcmd.c            |    3 +
 src/backend/tcop/utility.c                    |    6 +-
 src/backend/utils/adt/pseudotypes.c           |   25 +
 src/backend/utils/cache/relcache.c            |   53 +-
 src/backend/utils/misc/guc.c                  |    1 +
 src/bin/pg_dump/pg_dump.c                     |  117 +-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/psql/describe.c                       |  112 +-
 src/include/access/reloptions.h               |    2 +-
 src/include/access/seqamapi.h                 |  109 ++
 src/include/catalog/heap.h                    |    1 +
 src/include/catalog/pg_am.h                   |    5 +
 src/include/catalog/pg_proc.h                 |   19 +
 src/include/catalog/pg_type.h                 |    7 +
 src/include/commands/defrem.h                 |    1 +
 src/include/commands/sequence.h               |   31 +-
 src/include/commands/tablecmds.h              |    2 +-
 src/include/nodes/nodes.h                     |    1 +
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/utils/builtins.h                  |    2 +
 src/include/utils/rel.h                       |    8 +-
 src/test/regress/expected/create_am.out       |   19 +
 src/test/regress/expected/create_view.out     |    5 +-
 src/test/regress/expected/opr_sanity.out      |    4 +-
 src/test/regress/expected/sequence.out        |   35 +-
 src/test/regress/expected/updatable_views.out |   95 +-
 src/test/regress/sql/create_am.sql            |   16 +
 src/test/regress/sql/create_view.sql          |    5 +-
 src/test/regress/sql/opr_sanity.sql           |    4 +-
 src/test/regress/sql/sequence.sql             |   10 +-
 56 files changed, 2699 insertions(+), 764 deletions(-)
 create mode 100644 doc/src/sgml/seqam.sgml
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqamapi.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqamapi.h

diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 30adece..fd355dc 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 7e82cdc..d37058a 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -251,6 +251,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
index 3c091f8..8676a84 100644
--- a/doc/src/sgml/ref/create_access_method.sgml
+++ b/doc/src/sgml/ref/create_access_method.sgml
@@ -61,7 +61,8 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
     <listitem>
      <para>
       This clause specifies type of access method to define.
-      Only <literal>INDEX</literal> is supported at present.
+      Supported values are <literal>INDEX</literal> for index access methods
+      and <literal>SEQUENCE</literal> for sequence access methods.
      </para>
     </listitem>
    </varlistentry>
@@ -75,7 +76,9 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
       of access method to the core.  The handler function must take single
       argument of type <type>internal</>, and its return type depends on the
       type of access method; for <literal>INDEX</literal> access methods, it
-      must be <type>index_am_handler</type>.
+      must be <type>index_am_handler</type> and for
+      <literal>SEQUENCE</literal> access methods it must be
+      <type>sequence_am_handler</type>.
      </para>
 
      <para>
@@ -114,6 +117,8 @@ CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;
    <member><xref linkend="sql-drop-access-method"></member>
    <member><xref linkend="sql-createopclass"></member>
    <member><xref linkend="sql-createopfamily"></member>
+   <member><xref linkend="index-api"> </member>
+   <member><xref linkend="seqam"></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..5faeae3
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,312 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_am</structname> system catalog (see
+   <xref linkend="catalog-pg-am">).
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The SQL interface of the sequence access method consists of a handler
+   function which has to return special pseudo-type
+   <type>sequence_handler</type>. This function must be written in a compiled
+   language such as C, using the version-1 interface. For details on C
+   language calling conventions and dynamic loading, see
+   <xref linkend="xfunc-c">.
+  </para>
+
+  <para>
+   The actual C structure returned by the handler function is following:
+<programlisting>
+typedef struct SeqAmRoutine
+{
+    NodeTag        type;
+
+    /* Custom datatype used for storing state of the sequence by the AM */
+    Oid            StateTypeOid;
+
+    /* Function for parsing reloptions */
+    ParseRelOptions_function    amoptions;
+
+    /* Initialization */
+    SeqAMInit_function          Init;
+
+    /* nextval handler */
+    SeqAMAlloc_function         Alloc;
+
+    /* State manipulation functions */
+    SeqAMSetval_function        Setval;
+    SeqAMGetState_function      GetState;
+    SeqAMSetState_function      SetState;
+} SeqAmRoutine;
+</programlisting>
+  </para>
+
+  <para>
+   These are the functions implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+Datum
+SeqAMInit (Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+           bool restart_requested, bool is_init)
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   The <literal>seq</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the Datum of a state of the sequence. The type
+   of the Datum is the <structname>StateTypeOid</structname> type.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+ParseRelOptions (Datum reloptions, bool validate)
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+SeqAMAlloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+            int64 *last)
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum SeqAMGetState (Relation seqrel, SequenceHandle *seqh)
+</programlisting>
+   Dump the current state of the sequence. The return value is the Datum of
+   a <structname>StateTypeOid</structname>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetState (Relation seqrel, SequenceHandle *seqh, Datum amstate)
+</programlisting>
+   Restore state of the sequence based on amstate which is Datum of a
+   <structname>StateTypeOid</structname> type.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following helper functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh)
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+Form_pg_sequence
+(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns the whole pg_sequence tuple as the
+   <structname>Form_pg_sequence</structname> structure.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum
+sequence_read_state(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns just the sequence access method specific state Datum.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_save_state(SequenceHandle *seqh, Datum seqstate, bool dowal)
+</programlisting>
+   Save the modified sequence state tuple indicating if the change should be
+   WAL logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh)
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_save_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh)
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_options</>
+   and/or added by <function>sequence_read_state</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_save_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors)
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname)
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a..223403d 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,7 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  tablesample transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc sequence \
+			  spgist tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ea0755a..bb368d2 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -888,8 +888,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
- * AM's options parser function in the case of a tuple corresponding to an
- * index, or NULL otherwise.
+ * or sequence AM's options parser function in the case of a tuple corresponding
+ * to an index or sequence respectively, or NULL otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
@@ -921,7 +921,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1374,14 +1375,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for access methods.
  *
- *	amoptions	index AM's option parser function
+ *	amoptions	AM's option parser function
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+am_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 {
 	Assert(amoptions != NULL);
 
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..1784799
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqamapi.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqamapi.c b/src/backend/access/sequence/seqamapi.c
new file mode 100644
index 0000000..bfdd589
--- /dev/null
+++ b/src/backend/access/sequence/seqamapi.c
@@ -0,0 +1,191 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqamapi.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amstate.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_am.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * GetSeqAmRoutine - call the specified access method handler routine
+ * to get its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutine(Oid amhandler)
+{
+	Datum			datum;
+	SeqAmRoutine   *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SeqAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SeqAmRoutine))
+		elog(ERROR, "sequence access method handler function %u did not return an SeqAmRoutine struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * GetSeqAmRoutineByAMId - look up the handler of the sequence access method
+ * for the given OID, and retrieve its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineByAMId(Oid amoid)
+{
+	HeapTuple		tuple;
+	regproc			amhandler;
+	Form_pg_am		amform;
+
+	/* 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);
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("sequence access method \"%s\" does not have a handler",
+						NameStr(amform->amname))));
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function. */
+	return GetSeqAmRoutine(amhandler);
+}
+
+/*
+ * GetSeqAmRoutineForRelation - look up the handler of the sequence access
+ * method for the given sequence Relation.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineForRelation(Relation relation)
+{
+	SeqAmRoutine *seqam;
+	SeqAmRoutine *cseqam;
+
+	if (relation->rd_seqamroutine == NULL)
+	{
+		if (!OidIsValid(relation->rd_rel->relam))
+		{
+			char *relname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(relation)),
+													   RelationGetRelationName(relation));
+			elog(ERROR, "relation %s isn't sequence", relname);
+		}
+
+		seqam = GetSeqAmRoutineByAMId(relation->rd_rel->relam);
+		/* Save the data for later reuse in CacheMemoryContext */
+		cseqam = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												   sizeof(SeqAmRoutine));
+		memcpy(cseqam, seqam, sizeof(SeqAmRoutine));
+		relation->rd_seqamroutine = cseqam;
+	}
+
+	return relation->rd_seqamroutine;
+}
+
+/*
+ * ValidateSeqAmRoutine - to sanity check for the SeqAmRoutine returned by a
+ * handler.
+ *
+ * At the moment we only check that the StateTypeOid is valid type oid and that
+ * the type it points to is fixed width and will fit into the sequence page.
+ */
+void
+ValidateSeqAmRoutine(SeqAmRoutine *routine)
+{
+#define MAX_SEQAM_STATE_LEN \
+	MaxHeapTupleSize - MinHeapTupleSize - MAXALIGN(offsetof(FormData_pg_sequence, amstate))
+
+	Oid	stattypeoid = routine->StateTypeOid;
+
+	if (!get_typisdefined(stattypeoid))
+		ereport(ERROR,
+				(errmsg("type with OID %u does not exist",
+						stattypeoid)));
+
+	if (TypeIsToastable(stattypeoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" must not be toastable",
+						format_type_be(stattypeoid))));
+
+	if (get_typlen(stattypeoid) >= MAX_SEQAM_STATE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" exceeds maximum allowed length (%lu)",
+						format_type_be(stattypeoid), MAX_SEQAM_STATE_LEN)));
+
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..2d31478
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,408 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+#include "utils/typcache.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+static bytea *seqam_local_reloptions(Datum reloptions, bool validate);
+static Datum seqam_local_init(Relation seqrel, Form_pg_sequence seq,
+							  int64 restart_value, bool restart_requested,
+							  bool is_init);
+static int64 seqam_local_alloc(Relation seqrel, SequenceHandle *seqh,
+							   int64 nrequested, int64 *last);
+static void seqam_local_setval(Relation seqrel, SequenceHandle *seqh,
+							   int64 new_value);
+static Datum seqam_local_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+								  Datum amstate);
+
+static char *
+seqam_local_state_get_part(StringInfo buf, char *ptr, bool *found)
+{
+	*found = false;
+
+	resetStringInfo(buf);
+
+	while (*ptr != ',' && *ptr != ')')
+	{
+		char		ch = *ptr++;
+
+		if (ch == '\0')
+		{
+			*found = false;
+			return ptr;
+		}
+
+		appendStringInfoChar(buf, ch);
+
+		if (!*found)
+			*found = true;
+	}
+
+	return ptr;
+}
+
+Datum
+seqam_local_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr;
+	Datum		d;
+	bool		found;
+	StringInfoData		buf;
+	LocalSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	initStringInfo(&buf);
+
+	/* last_value is required */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (!found)
+		goto malformed;
+	d = DirectFunctionCall1(int8in, CStringGetDatum(buf.data));
+	state->last_value = DatumGetInt64(d);
+	if (*ptr == ',')
+		ptr++;
+
+	/* is_called is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(boolin, CStringGetDatum(buf.data));
+		state->is_called = DatumGetBool(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->is_called = true;
+
+	/* log_cnt is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(int4in, CStringGetDatum(buf.data));
+		state->log_cnt = DatumGetInt32(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->log_cnt = 0;
+
+	if (*ptr != ')' || *(++ptr) != '\0')
+		goto malformed;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_local_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_local_state_out(PG_FUNCTION_ARGS)
+{
+	LocalSequenceState *state = (LocalSequenceState *) PG_GETARG_POINTER(0);
+	StringInfoData		buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s,%d)",
+					 state->last_value, state->is_called ? "t" : "f",
+					 state->log_cnt);
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the local sequence access method.
+ */
+Datum
+seqam_local_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine *seqam = makeNode(SeqAmRoutine);
+
+	seqam->StateTypeOid = SEQAMLOCALSTATEOID;
+	seqam->amoptions = seqam_local_reloptions;
+	seqam->Init = seqam_local_init;
+	seqam->Alloc = seqam_local_alloc;
+	seqam->Setval = seqam_local_setval;
+	seqam->GetState = seqam_local_get_state;
+	seqam->SetState = seqam_local_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+static bytea *
+seqam_local_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+static Datum
+seqam_local_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	LocalSequenceState	   *localseq;
+
+	if (is_init)
+		localseq = palloc0(sizeof(LocalSequenceState));
+	else
+		localseq = (LocalSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		localseq->last_value = restart_value;
+		localseq->is_called = false;
+	}
+
+	localseq->log_cnt = 0;
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+static int64
+seqam_local_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+				  int64 *last)
+{
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) seq->amstate;
+
+	next = result = localseq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = localseq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_start_update(seqh, logit);
+
+	if (logit)
+	{
+		localseq->last_value = next;
+		localseq->log_cnt = 0;
+		localseq->is_called = true;
+
+		sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	}
+
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_save_state(seqh, PointerGetDatum(localseq), false);
+
+	sequence_finish_update(seqh);
+
+	return result;
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+static void
+seqam_local_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq;
+
+	amstate = sequence_read_state(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	localseq->last_value = new_value;		/* last fetched number */
+	localseq->is_called = true;
+	localseq->log_cnt = 0;			/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+static Datum
+seqam_local_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq = palloc(sizeof(LocalSequenceState));
+
+	amstate = sequence_read_state(seqh);
+	memcpy(localseq, DatumGetPointer(amstate), sizeof(LocalSequenceState));
+	sequence_release_tuple(seqh);
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+static void
+seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+					  Datum amstate)
+{
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(localseq->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 41d2fd4..e684f60 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -251,6 +251,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e997b57..b7cd40a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -41,6 +41,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
@@ -88,6 +89,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -849,6 +851,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -922,6 +925,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1035,6 +1039,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1263,6 +1268,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1275,7 +1281,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the pg_am record.
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1330,6 +1337,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = AccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index cb3ba85..b40f0c7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index f40a005..40b5e91 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -291,6 +291,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 7a93754..58261d4 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -15,6 +15,8 @@
 
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_am.h"
@@ -29,7 +31,7 @@
 #include "utils/syscache.h"
 
 
-static Oid	lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid	lookup_am_handler_func(List *handler_name, char amtype);
 static char *get_am_type_string(char amtype);
 
 
@@ -72,7 +74,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	/*
 	 * Get the handler function oid, verifying the AM type while at it.
 	 */
-	amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+	amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
 
 	/*
 	 * Insert tuple into pg_am.
@@ -187,6 +189,16 @@ get_index_am_oid(const char *amname, bool missing_ok)
 }
 
 /*
+ * get_seq_am_oid - given an access method name, look up its OID
+ *		and verify it corresponds to an sequence AM.
+ */
+Oid
+get_seq_am_oid(const char *amname, bool missing_ok)
+{
+	return get_am_type_oid(amname, AMTYPE_SEQUENCE, missing_ok);
+}
+
+/*
  * get_am_oid - given an access method name, look up its OID.
  * 		The type is not checked.
  */
@@ -226,6 +238,8 @@ get_am_type_string(char amtype)
 	{
 		case AMTYPE_INDEX:
 			return "INDEX";
+		case AMTYPE_SEQUENCE:
+			return "SEQUENCE";
 		default:
 			/* shouldn't happen */
 			elog(ERROR, "invalid access method type '%c'", amtype);
@@ -239,9 +253,10 @@ get_am_type_string(char amtype)
  * This function either return valid function Oid or throw an error.
  */
 static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
 {
 	Oid			handlerOid;
+	Oid			handler_type;
 	static const Oid funcargtypes[1] = {INTERNALOID};
 
 	if (handler_name == NIL)
@@ -256,16 +271,21 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
 	switch (amtype)
 	{
 		case AMTYPE_INDEX:
-			if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
-				ereport(ERROR,
-						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-						 errmsg("function %s must return type \"%s\"",
-								NameListToString(handler_name),
-								"index_am_handler")));
+			handler_type = INDEX_AM_HANDLEROID;
+			break;
+		case AMTYPE_SEQUENCE:
+			handler_type = SEQ_AM_HANDLEROID;
 			break;
 		default:
 			elog(ERROR, "unrecognized access method type \"%c\"", amtype);
 	}
 
+	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;
 }
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5cb28cf..54a22da 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -680,6 +680,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cb7a145..ab7b7a3 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 13b04e6..92ae4db 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -536,7 +536,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..ad4159b 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,16 +14,22 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/tupmacs.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +42,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,24 +82,112 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	Oid			statetyp;
+	int16		statetyplen;
+	bool		statetypbyval;
+	HeapTupleData tup;
+	bool		inupdate;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
  */
 static SeqTableData *last_used_seq = NULL;
 
+static HeapTuple build_seq_tuple(Relation rel, SeqAmRoutine *seqam,
+								 Form_pg_sequence new, int64 restart_value);
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+
+/*
+ * Build template column definition for a sequence relation.
+*/
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_not_null = true;
+	coldef->is_from_type = false;
+	/* Force plain storage. */
+	coldef->storage = 'p';
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(Oid amstateTypeOid)
+{
+	List	   *seqcols;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		switch (colid)
+		{
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+			case SEQ_COL_AMSTATE:
+				coldef->typeName = makeTypeNameFromOid(amstateTypeOid, -1);
+				coldef->colname = "amstate";
+				break;
+		}
+
+		seqcols = lappend(seqcols, coldef);
+	}
 
+	return seqcols;
+}
 
 /*
  * DefineSequence
@@ -111,14 +200,12 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
+	List	   *seqcols;
+	SeqAmRoutine *seqam;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -147,102 +234,33 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
-	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
-
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
-		{
-			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
-				break;
-			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
-				break;
-			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
-				break;
-			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
-				break;
-			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
-				break;
-			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
-			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
-				break;
-		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
-	}
+	if (seq->accessMethod)
+		seqamid = get_seq_am_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
+	seqam = GetSeqAmRoutineByAMId(seqamid);
+	/* Build column definitions. */
+	seqcols = BuildSeqColumnDefList(seqam->StateTypeOid);
 
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
+	stmt->tableElts = seqcols;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	/* Build new sequence tuple and store it. */
+	tuple = build_seq_tuple(rel, seqam, &new, new.start_value);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +272,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +286,89 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
+	TupleDesc			tupDesc;
+	Datum	   values[SEQ_COL_LASTCOL];
+	bool	   nulls[SEQ_COL_LASTCOL];
+	SeqAmRoutine *seqam;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
-	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tupDesc = RelationGetDescr(seqrel);
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(seqrel, seq, seq->start_value,
+											  true, false);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
+}
+
+/*
+ * Build sequence tuple based on the sequence form and fill in the
+ * sequence AM specific info as well.
+ */
+static HeapTuple
+build_seq_tuple(Relation rel, SeqAmRoutine *seqam, Form_pg_sequence new,
+				int64 restart_value)
+{
+	TupleDesc	tupDesc;
+	HeapTuple	tuple;
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+
+	tupDesc = RelationGetDescr(rel);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new->start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new->increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new->max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new->min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new->cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new->is_cycled);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(rel, new, restart_value,
+											  false, true);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	return tuple;
 }
 
 /*
@@ -361,7 +411,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +431,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,6 +439,69 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 }
 
 /*
+ * Replace the type of amstate column.
+ *
+ * We don't do AlterTable here as that produces dead columns which we don't
+ * want. This is safe because the sequence page is controlled by code in this
+ * module and isn't changed the same way as a table.
+ *
+ * TODO: check if anybody is depending on the row-type associated with the
+ * sequence.
+ */
+static void
+replace_sequence_amstate_col(Oid seqrelid, Oid typid)
+{
+	Relation	attr_rel;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	HeapTuple	tp,
+				attr_tuple,
+				newattr_tuple;
+	Form_pg_type	typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+
+	memset(nulls, 0, sizeof(nulls));
+	memset(replace, 0, sizeof(replace));
+
+	replace[Anum_pg_attribute_atttypid - 1] = true;
+	replace[Anum_pg_attribute_attlen - 1] = true;
+	replace[Anum_pg_attribute_attbyval - 1] = true;
+	replace[Anum_pg_attribute_attalign - 1] = true;
+
+	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(typid);
+	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(typtup->typlen);
+	values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(typtup->typbyval);
+	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(typtup->typalign);
+
+	/* Build DROP command for amstate of old AM. */
+	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+	attr_tuple = SearchSysCache2(ATTNUM,
+								 ObjectIdGetDatum(seqrelid),
+								 Int16GetDatum(SEQ_COL_AMSTATE));
+	if (!HeapTupleIsValid(attr_tuple))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 SEQ_COL_AMSTATE, seqrelid);
+
+	newattr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel),
+									  values, nulls, replace);
+	simple_heap_update(attr_rel, &newattr_tuple->t_self, newattr_tuple);
+	CatalogUpdateIndexes(attr_rel, newattr_tuple);
+
+	ReleaseSysCache(tp);
+	heap_freetuple(newattr_tuple);
+	ReleaseSysCache(attr_tuple);
+
+	heap_close(attr_rel, RowExclusiveLock);
+}
+
+/*
  * AlterSequence
  *
  * Modify the definition of a sequence relation
@@ -406,19 +509,24 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence seq,
+					 new;
 	List	   *owned_by;
 	ObjectAddress address;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
+	SeqAmRoutine *oldseqam;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +534,187 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
+	oldamid = seqrel->rd_rel->relam;
+	oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	if (stmt->accessMethod)
+		seqamid = get_seq_am_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(stmt->options, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		HeapTuple       seqamtup;
+		Form_pg_am		form_am;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
+		SeqAmRoutine	   *newseqam;
+
+		oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = oldseqam->Alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
+
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(AMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
+
+		newseqam = GetSeqAmRoutineByAMId(seqamid);
+
+		form_am = (Form_pg_am) GETSTRUCT(seqamtup);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		validnsps[0] = NameStr(form_am->amname);
+		validnsps[1] = NULL;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		(void) am_reloptions(newseqam->amoptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-		PageSetLSN(page, recptr);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
+
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
+
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * And change the type definition.
+		 *
+		 * We can't use AlterTable internals here because the sequence
+		 * has to have the expected number of columns and no
+		 * attisdropped = true columns.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		replace_sequence_amstate_col(seqrelid, newseqam->StateTypeOid);
+		CommandCounterIncrement();
+
+		/* Rebuild the sequence tuple and save it. */
+		tuple = build_seq_tuple(seqrel, newseqam, new, restart_value);
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										AccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = AccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
+	else
+	{
+		Datum	   newamstate;
 
-	END_CRIT_SECTION();
+		sequence_check_range(restart_value, new->min_value, new->max_value,
+							 restart_requested ? "RESTART" : "START");
 
-	UnlockReleaseBuffer(buf);
+		/* Let the new sequence AM initialize. */
+		newamstate = oldseqam->Init(seqrel, new, restart_value,
+									restart_requested, false);
+
+		sequence_start_update(&seqh, true);
+		memcpy(seq, new, offsetof(FormData_pg_sequence, amstate));
+		sequence_save_state(&seqh, newamstate, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +755,26 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +799,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam->Alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +816,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for syncrep.
-	 * It's sufficient to ensure the toplevel transaction has an xid, no need
-	 * to assign xids subxacts, that'll already trigger an appropriate wait.
-	 * (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +827,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +888,26 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,106 +924,86 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam->Setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	last_used_seq = elm;
 
-	UnlockReleaseBuffer(buf);
+	sequence_close(&seqh);
 
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	LocalSequenceState	state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
+
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-	do_setval(relid, next, true);
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Build the state and pass it to sequence AM. */
+	state.last_value = next;
+	state.log_cnt = 0;
+	state.is_called = iscalled;
+	seqam->SetState(seqh.rel, &seqh, PointerGetDatum(&state));
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-	PG_RETURN_INT64(next);
-}
+	/* Reset local cached data */
+	elm->cached = elm->last;
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1034,11 +1062,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1117,58 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->statetyp = 6025; /* TODO */
+	seqh->statetyplen = -1;
+	seqh->statetypbyval = false;
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(!seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1178,170 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+Form_pg_sequence
+sequence_read_options(SequenceHandle *seqh)
+{
+	return (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
+}
+
+Datum
+sequence_read_state(SequenceHandle *seqh)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+
+	return PointerGetDatum(seq->amstate);
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool dowal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+
+	if (seqh->statetyplen < 0)
+	{
+		get_typlenbyval(seqh->statetyp, &seqh->statetyplen,
+						&seqh->statetypbyval);
+		Assert(seqh->statetyplen > 0);
+	}
+
+	if (dowal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_save_state(SequenceHandle *seqh, Datum amstate, bool dowal)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+	Page				page;
+
+	/*
+	 * Update the state data inline.
+	 *
+	 * This is only needed when the provided amstate datum points to different
+	 * data than what is already in the tuple.
+	 */
+	if (DatumGetPointer(amstate) != seq->amstate)
+	{
+		if (seqh->statetypbyval)
+			store_att_byval(seq->amstate, amstate, seqh->statetyplen);
+		else
+			memmove(seq->amstate, DatumGetPointer(amstate), seqh->statetyplen);
+	}
+
+	page = BufferGetPage(seqh->buf);
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (dowal && RelationNeedsWAL(seqh->rel))
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	Assert(BufferIsValid(seqh->buf));
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1351,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1428,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1436,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1445,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1453,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1460,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1473,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1526,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1540,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1650,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1678,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1686,85 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Datum		state;
+	char	   *statestr;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typoutput;
+	bool		typisvarlena;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type output function. */
+	getTypeOutputInfo(seqam->StateTypeOid, &typoutput, &typisvarlena);
+
+	/* Get the output and convert it to string. */
+	state = seqam->GetState(seqh.rel, &seqh);
+	statestr = OidOutputFunctionCall(typoutput, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_TEXT_P(cstring_to_text(statestr));
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *statestr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typinput,
+				typioparam;
+	Datum		state;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type input function. */
+	getTypeInputInfo(seqam->StateTypeOid, &typinput, &typioparam);
+
+	/* Convert the string to the state type and set it as new state. */
+	state = OidInputFunctionCall(typinput, statestr, typioparam, -1);
+	seqam->SetState(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1830,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 96dc923..6e6ee5d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -269,6 +270,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -454,7 +456,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -473,7 +475,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -551,13 +552,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		SeqAmRoutine *seqam;
+
+		Assert(relamid != InvalidOid);
+		seqam = GetSeqAmRoutineByAMId(relamid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 NULL, true, false);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		(void) am_reloptions(seqam->amoptions, reloptions, true);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -678,6 +695,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3308,7 +3326,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW |
+								ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4308,6 +4327,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4351,8 +4373,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a table, view, or foreign table");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9403,12 +9425,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqamroutine->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
+					 errmsg("\"%s\" is not a table, view, materialized view, index, sequence, or TOAST table",
 							RelationGetRelationName(rel))));
 			break;
 	}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 227d382..c5b73c0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2115,7 +2115,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e9d9ba2..ca0f481 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6378db8..8c19b31 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3564,7 +3564,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3577,7 +3579,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 854c062..5972ca6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1627,7 +1627,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1638,7 +1640,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1273352..75af985 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -310,7 +310,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <list>	event_trigger_when_list event_trigger_value_list
 %type <defelt>	event_trigger_when_item
-%type <chr>		enable_trigger
+%type <chr>		enable_trigger am_type
 
 %type <str>		copy_file_name
 				database_name access_method_clause access_method attr_name
@@ -605,7 +605,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 METHOD 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
@@ -3587,7 +3588,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3609,7 +3636,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3617,11 +3668,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3680,7 +3754,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
@@ -4715,16 +4789,21 @@ row_security_cmd:
  *
  *****************************************************************************/
 
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
 				{
 					CreateAmStmt *n = makeNode(CreateAmStmt);
 					n->amname = $4;
 					n->handler_name = $8;
-					n->amtype = AMTYPE_INDEX;
+					n->amtype = $6;
 					$$ = (Node *) n;
 				}
 		;
 
+am_type:
+			INDEX		{ $$ = AMTYPE_INDEX; }
+		|	SEQUENCE	{ $$ = AMTYPE_SEQUENCE; }
+		;
+
 /*****************************************************************************
  *
  *		QUERIES :
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6528494..509fa32 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -29,6 +29,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -456,6 +457,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4d0aac9..8b95577 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -965,7 +965,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -998,7 +999,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index dd447cf..6c1456e 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -399,6 +399,31 @@ index_am_handler_out(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();			/* keep compiler quiet */
 }
 
+/*
+ * seq_am_handler_int		- input routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_in(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * seq_am_handler_out		- output routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_out(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot display a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
 
 /*
  * tsm_handler_in		- input routine for pseudo-type TSM_HANDLER.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 130c06d..b9686da 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -260,6 +261,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -424,6 +426,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	amoptions_function	amoptions = NULL;
 
 	relation->rd_options = NULL;
 
@@ -432,10 +435,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_amroutine->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqamroutine->amoptions;
+			break;
 		default:
 			return;
 	}
@@ -447,8 +455,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_amroutine->amoptions : NULL);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1049,11 +1056,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1555,6 +1565,34 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation relation)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	SeqAmRoutine	   *result, *tmp;
+
+	/*
+	 * Look up the index's access method, save the OID of its handler function
+	 */
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 relation->rd_rel->relam);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amtype == AMTYPE_SEQUENCE);
+	relation->rd_amhandler = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	result = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												 sizeof(SeqAmRoutine));
+	tmp = GetSeqAmRoutine(relation->rd_amhandler);
+	memcpy(result, tmp, sizeof(SeqAmRoutine));
+	relation->rd_seqamroutine = result;
+}
 
 /*
  *		formrdesc
@@ -4885,6 +4923,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqamroutine == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 65a6cd4..074406a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqamapi.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index b3ef201..513ee4a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4749,6 +4749,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4800,6 +4801,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4842,6 +4844,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4884,6 +4887,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4926,6 +4930,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4966,6 +4971,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5005,6 +5011,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5044,6 +5051,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5083,6 +5091,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5121,6 +5130,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5155,6 +5165,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5184,6 +5195,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5223,6 +5235,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5276,6 +5289,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5341,6 +5355,10 @@ getTables(Archive *fout, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -11579,6 +11597,9 @@ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
 		case AMTYPE_INDEX:
 			appendPQExpBuffer(q, "TYPE INDEX ");
 			break;
+		case AMTYPE_SEQUENCE:
+			appendPQExpBuffer(q, "TYPE SEQUENCE ");
+			break;
 		default:
 			write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
 					  aminfo->amtype, qamname);
@@ -15282,7 +15303,8 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -15362,6 +15384,37 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.6 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.amname\n"
+								 "FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -15402,6 +15455,13 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -15468,6 +15528,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15484,16 +15547,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.6 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15506,14 +15582,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 66e6931..66af1c4 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -231,6 +231,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_class) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index fd8dc91..3ca6ebe 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1403,30 +1403,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1470,13 +1446,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1805,6 +1823,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1814,12 +1834,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.amname\n"
+								"FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 469ac67..69135aa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -273,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
+extern bytea *am_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqamapi.h b/src/include/access/seqamapi.h
new file mode 100644
index 0000000..a4c826a
--- /dev/null
+++ b/src/include/access/seqamapi.h
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqamapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAMAPI_H
+#define SEQAMAPI_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef bytea * (*ParseRelOptions_function) (Datum reloptions,
+											 bool validate);
+
+typedef Datum (*SeqAMInit_function) (Relation seqrel,
+									 Form_pg_sequence seq,
+									 int64 restart_value,
+									 bool restart_requested,
+									 bool is_init);
+
+typedef int64 (*SeqAMAlloc_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 nrequested,
+									  int64 *last);
+
+typedef void (*SeqAMSetval_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 new_value);
+
+typedef Datum (*SeqAMGetState_function) (Relation seqrel,
+										 SequenceHandle *seqh);
+typedef void (*SeqAMSetState_function) (Relation seqrel, SequenceHandle *seqh,
+										Datum amstate);
+
+typedef struct SeqAmRoutine
+{
+	NodeTag		type;
+
+	/* Custom columns needed by the AM */
+	Oid			StateTypeOid;
+
+	/* Function for parsing reloptions */
+	ParseRelOptions_function	amoptions;
+
+	/* Initialization */
+	SeqAMInit_function			Init;
+
+	/* nextval handler */
+	SeqAMAlloc_function			Alloc;
+
+	/* State manipulation functions */
+	SeqAMSetval_function		Setval;
+	SeqAMGetState_function		GetState;
+	SeqAMSetState_function		SetState;
+} SeqAmRoutine;
+
+extern SeqAmRoutine *GetSeqAmRoutine(Oid seqamhandler);
+extern SeqAmRoutine *GetSeqAmRoutineByAMId(Oid amoid);
+extern SeqAmRoutine *GetSeqAmRoutineForRelation(Relation relation);
+extern void ValidateSeqAmRoutine(SeqAmRoutine *routine);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern Form_pg_sequence sequence_read_options(SequenceHandle *seqh);
+extern Datum sequence_read_state(SequenceHandle *seqh);
+extern void sequence_start_update(SequenceHandle *seqh, bool dowal);
+extern void sequence_save_state(SequenceHandle *seqh, Datum seqstate,
+								bool dowal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+/* We need this public for serval3 */
+typedef struct LocalSequenceState
+{
+	int64   last_value;
+	int32	log_cnt;
+	bool	is_called;
+} LocalSequenceState;
+
+extern Datum seqam_local_state_in(PG_FUNCTION_ARGS);
+extern Datum seqam_local_state_out(PG_FUNCTION_ARGS);
+extern Datum seqam_local_handler(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAMAPI_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..d6b3cd7 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 1116923..9c615f7 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
  * ----------------
  */
 #define AMTYPE_INDEX					'i'		/* index access method */
+#define	AMTYPE_SEQUENCE					'S'		/* sequence access method */
 
 /* ----------------
  *		initial contents of pg_am
@@ -84,4 +85,8 @@ DATA(insert OID = 3580 (  brin		brinhandler i ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
+DATA(insert OID = 6023 (  local		seqam_local_handler	S ));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6023
+
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a595327..c8f86f2 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1759,6 +1759,10 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v u 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 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 25 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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 s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3690,6 +3694,10 @@ DATA(insert OID = 326  (  index_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f
 DESCR("I/O");
 DATA(insert OID = 327  (  index_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
 DESCR("I/O");
+DATA(insert OID = 6021  (  seq_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 6020 "2275" _null_ _null_ _null_ _null_ _null_ seq_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6022 (  seq_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6020" _null_ _null_ _null_ _null_ _null_ seq_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
 DATA(insert OID = 3311 (  tsm_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 3312 (  tsm_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
@@ -3701,6 +3709,10 @@ DESCR("BERNOULLI tablesample method handler");
 DATA(insert OID = 3314 (  system			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_system_handler _null_ _null_ _null_ ));
 DESCR("SYSTEM tablesample method handler");
 
+/* sequence access method handlers */
+DATA(insert OID = 6024 (  seqam_local_handler	   PGNSP PGUID 12 1 0 0 0 f f f f f f v s 1 0 6020 "2281" _null_ _null_ _null_ _null_ _null_ seqam_local_handler _null_ _null_ _null_ ));
+DESCR("Local SequenceAM handler");
+
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
 DESCR("MD5 hash");
@@ -5259,6 +5271,13 @@ DESCR("pg_controldata recovery state information as a function");
 DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
 DESCR("pg_controldata init state information as a function");
 
+/* Sequence AM */
+DATA(insert OID = 6027 (  seqam_local_state_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 6025 "2275" _null_ _null_ _null_ _null_ _null_ seqam_local_state_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6028 (  seqam_local_state_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6025" _null_ _null_ _null_ _null_ _null_ seqam_local_state_out _null_ _null_ _null_ ));
+DESCR("I/O");
+
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2c90b76..40214e3 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -696,11 +696,18 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 #define FDW_HANDLEROID	3115
 DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define INDEX_AM_HANDLEROID	325
+DATA(insert OID = 6020 ( seq_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 seq_am_handler_in seq_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQ_AM_HANDLEROID	6020
 DATA(insert OID = 3310 ( tsm_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define TSM_HANDLEROID	3310
 DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 #define ANYRANGEOID		3831
 
+/* Sequence AM composite types */
+DATA(insert OID = 6025 ( seqam_local_state	PGNSP PGUID 16 f b U f t \054 0 0 6026 seqam_local_state_in seqam_local_state_out - - - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQAMLOCALSTATEOID 6025
+DATA(insert OID = 6026 ( _seqam_local_state	PGNSP PGUID -1 f b A f t \054 0 6025 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 
 /*
  * macros
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index b064eb4..c9bf431 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -139,6 +139,7 @@ extern Datum transformGenericOptions(Oid catalogId,
 extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
 extern void RemoveAccessMethodById(Oid amOid);
 extern Oid	get_index_am_oid(const char *amname, bool missing_ok);
+extern Oid	get_seq_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);
 
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..e0f6439 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -19,20 +19,18 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "storage/relfilenode.h"
-
+#include "access/htup_details.h"
 
 typedef struct FormData_pg_sequence
 {
-	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
+	/* amstate follows */
+	char		amstate[FLEXIBLE_ARRAY_MEMBER];
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,19 +39,16 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  * Columns of a sequence relation
  */
 
-#define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_STARTVAL		1
+#define SEQ_COL_INCBY			2
+#define SEQ_COL_MAXVALUE		3
+#define SEQ_COL_MINVALUE		4
+#define SEQ_COL_CACHE			5
+#define SEQ_COL_CYCLE			6
+#define SEQ_COL_AMSTATE			7
 
-#define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_FIRSTCOL		SEQ_COL_STARTVAL
+#define SEQ_COL_LASTCOL			SEQ_COL_AMSTATE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +67,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 7a770f4..1bceec8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 734df77..752d3a1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -477,6 +477,7 @@ typedef enum NodeTag
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
+	T_SeqAmRoutine,				/* in access/seqamapi.h */
 	T_TsmRoutine				/* in access/tsmapi.h */
 } NodeTag;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8b958b4..859fbad 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2205,8 +2205,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2214,8 +2216,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 206288d..1c89b49 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -584,6 +584,8 @@ extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
 extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_in(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_out(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
 extern Datum internal_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f2bebf2..4fe7e52 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -106,13 +106,18 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	Oid			rd_amhandler;	/* OID of access method's handler function */
+
+	/* These are non-NULL only for a sequence relation */
+	struct SeqAmRoutine *rd_seqamroutine;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
@@ -126,7 +131,6 @@ typedef struct RelationData
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
-	Oid			rd_amhandler;	/* OID of index AM's handler function */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
 	/* use "struct" here to avoid needing to include amapi.h: */
 	struct IndexAmRoutine *rd_amroutine;		/* index AM's API struct */
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index 47d6024..9242982 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -106,3 +106,22 @@ drop cascades to index grect2ind
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+-- Checks for sequence access methods
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+ERROR:  cannot drop access method local2 because other objects depend on it
+DETAIL:  sequence test_seqam depends on access method local2
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ae50c94..f1b1fc2 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -129,8 +129,9 @@ NOTICE:  view "v12_temp" will be a temporary view
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 NOTICE:  view "v13_temp" will be a temporary view
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 7c09fa3..ab0d299 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1586,7 +1586,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
  oid | amname | oid | proname 
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3a19cde 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate 
+-------------+--------------+---------------------+-----------+-------------+-----------+---------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (1,f,0)
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate  
+-------------+--------------+---------------------+-----------+-------------+-----------+----------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (2,t,31)
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c5dfbb5..a30582e 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,52 @@ SELECT table_name, column_name, is_updatable
   FROM information_schema.columns
  WHERE table_name LIKE E'r_\\_view%'
  ORDER BY table_name, ordinal_position;
- table_name |  column_name  | is_updatable 
-------------+---------------+--------------
- ro_view1   | a             | NO
- ro_view1   | b             | NO
- ro_view10  | a             | NO
- ro_view11  | a             | NO
- ro_view11  | b             | NO
- ro_view12  | a             | NO
- ro_view13  | a             | NO
- ro_view13  | b             | NO
- ro_view17  | a             | NO
- ro_view17  | b             | NO
- ro_view18  | a             | NO
- ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
- ro_view19  | start_value   | NO
- ro_view19  | increment_by  | NO
- ro_view19  | max_value     | NO
- ro_view19  | min_value     | NO
- ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
- ro_view19  | is_cycled     | NO
- ro_view19  | is_called     | NO
- ro_view2   | a             | NO
- ro_view2   | b             | NO
- ro_view20  | a             | NO
- ro_view20  | b             | NO
- ro_view20  | g             | NO
- ro_view3   | ?column?      | NO
- ro_view4   | count         | NO
- ro_view5   | a             | NO
- ro_view5   | rank          | NO
- ro_view6   | a             | NO
- ro_view6   | b             | NO
- ro_view7   | a             | NO
- ro_view7   | b             | NO
- ro_view8   | a             | NO
- ro_view8   | b             | NO
- ro_view9   | a             | NO
- ro_view9   | b             | NO
- rw_view14  | ctid          | NO
- rw_view14  | a             | YES
- rw_view14  | b             | YES
- rw_view15  | a             | YES
- rw_view15  | upper         | NO
- rw_view16  | a             | YES
- rw_view16  | b             | YES
- rw_view16  | aa            | YES
-(46 rows)
+ table_name | column_name  | is_updatable 
+------------+--------------+--------------
+ ro_view1   | a            | NO
+ ro_view1   | b            | NO
+ ro_view10  | a            | NO
+ ro_view11  | a            | NO
+ ro_view11  | b            | NO
+ ro_view12  | a            | NO
+ ro_view13  | a            | NO
+ ro_view13  | b            | NO
+ ro_view17  | a            | NO
+ ro_view17  | b            | NO
+ ro_view18  | a            | NO
+ ro_view19  | start_value  | NO
+ ro_view19  | increment_by | NO
+ ro_view19  | max_value    | NO
+ ro_view19  | min_value    | NO
+ ro_view19  | cache_value  | NO
+ ro_view19  | is_cycled    | NO
+ ro_view19  | amstate      | NO
+ ro_view2   | a            | NO
+ ro_view2   | b            | NO
+ ro_view20  | a            | NO
+ ro_view20  | b            | NO
+ ro_view20  | g            | NO
+ ro_view3   | ?column?     | NO
+ ro_view4   | count        | NO
+ ro_view5   | a            | NO
+ ro_view5   | rank         | NO
+ ro_view6   | a            | NO
+ ro_view6   | b            | NO
+ ro_view7   | a            | NO
+ ro_view7   | b            | NO
+ ro_view8   | a            | NO
+ ro_view8   | b            | NO
+ ro_view9   | a            | NO
+ ro_view9   | b            | NO
+ rw_view14  | ctid         | NO
+ rw_view14  | a            | YES
+ rw_view14  | b            | YES
+ rw_view15  | a            | YES
+ rw_view15  | upper        | NO
+ rw_view16  | a            | YES
+ rw_view16  | b            | YES
+ rw_view16  | aa           | YES
+(43 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index e2051c5..e811ea3 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -71,3 +71,19 @@ DROP ACCESS METHOD gist2 CASCADE;
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+
+-- Checks for sequence access methods
+
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 58d361d..e967fff 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -131,8 +131,9 @@ CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 6c9784a..6c44d70 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1055,7 +1055,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
 
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..c2b1164 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
-- 
1.9.1

#110Fabrízio de Royes Mello
fabriziomello@gmail.com
In reply to: Petr Jelinek (#109)
Re: Sequence Access Method WIP

On Thu, Mar 24, 2016 at 6:12 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

Hi,

I rebased this on top of the recently committed CREATE ACCESS METHOD.

Hi,

I got the above error trying to apply to the current master:

$ git apply /home/fabrizio/Downloads/0001-seqam-2016-03-24.patch
error: patch failed: src/backend/commands/amcmds.c:29
error: src/backend/commands/amcmds.c: patch does not apply

There are a wrong definition at the beginning of the amcmds.c:

34 <<<<<<< ours
35 static Oid lookup_index_am_handler_func(List *handler_name, char
amtype);
36 static const char *get_am_type_string(char amtype);
37 =======
38 static Oid lookup_am_handler_func(List *handler_name, char amtype);
39 static char *get_am_type_string(char amtype);
40 >>>>>>> theirs

After this small fix I can build and ran regress tests without errors.

But running "check-world" I got the error:

make[1]: Leaving directory `/data/postgresql/src/test/regress'
make: Leaving directory `/data/postgresql'
+ pg_dumpall -f /data/postgresql/src/bin/pg_upgrade/tmp_check/dump1.sql
ok 9 - dropuser foobar1 exit code 0
ok 10 - SQL DROP ROLE run: SQL found in server log
ok 11 - fails with nonexistent user
ok
t/080_pg_isready.pl .......
1..10
ok 1 - pg_isready --help exit code 0
ok 2 - pg_isready --help goes to stdout
ok 3 - pg_isready --help nothing to stderr
ok 4 - pg_isready --version exit code 0
ok 5 - pg_isready --version goes to stdout
ok 6 - pg_isready --version nothing to stderr
ok 7 - pg_isready with invalid option nonzero exit code
ok 8 - pg_isready with invalid option prints error message
ok 9 - fails with no server running
pg_dump: [archiver (db)] query failed: ERROR:  column "sequence_name" does
not exist
LINE 1: SELECT sequence_name, start_value, increment_by, CASE WHEN i...
               ^
pg_dump: [archiver (db)] query was: SELECT sequence_name, start_value,
increment_by, CASE WHEN increment_by > 0 AND max_value =
9223372036854775807 THEN NULL      WHEN increment_by < 0 AND max_value = -1
THEN NULL      ELSE max_value END AS max_value, CASE WHEN increment_by > 0
AND min_value = 1 THEN NULL      WHEN increment_by < 0 AND min_value =
-9223372036854775807 THEN NULL      ELSE min_value END AS min_value,
cache_value, is_cycled FROM check_seq
pg_dumpall: pg_dump failed on database "regression", exiting
+ pg_dumpall1_status=1
+ [ /data/postgresql != /data/postgresql ]
+
/data/postgresql/src/bin/pg_upgrade/tmp_check/install//home/fabrizio/pgsql/bin/pg_ctl
-m fast stop
waiting for server to shut down.... done
server stopped
+ [ -n  ]
+ [ -n  ]
+ [ -n 1 ]
+ echo pg_dumpall of pre-upgrade database cluster failed
pg_dumpall of pre-upgrade database cluster failed
+ exit 1
+ rm -rf /tmp/pg_upgrade_check-3NUa0X
make[2]: *** [check] Error 1
make[2]: Leaving directory `/data/postgresql/src/bin/pg_upgrade'
make[1]: *** [check-pg_upgrade-recurse] Error 2
make[1]: *** Waiting for unfinished jobs....

And testing pg_dump itself I got the same error trying to dump a database
that contains a sequence.

fabrizio=# create sequence x;
CREATE SEQUENCE
fabrizio=# \ds
List of relations
Schema | Name | Type | Owner
--------+------+----------+----------
public | x | sequence | fabrizio
(1 row)

fabrizio=# \d x
Sequence "public.x"
Column | Type | Value
--------------+-------------------+---------------------
start_value | bigint | 1
increment_by | bigint | 1
max_value | bigint | 9223372036854775807
min_value | bigint | 1
cache_value | bigint | 1
is_cycled | boolean | f
amstate | seqam_local_state | (1,f,0)
Access Method: local

fabrizio=# \q

fabrizio@bagual:~/pgsql
$ bin/pg_dump > /tmp/fabrizio.sql
pg_dump: [archiver (db)] query failed: ERROR: column "sequence_name" does
not exist
LINE 1: SELECT sequence_name, start_value, increment_by, CASE WHEN i...
^
pg_dump: [archiver (db)] query was: SELECT sequence_name, start_value,
increment_by, CASE WHEN increment_by > 0 AND max_value =
9223372036854775807 THEN NULL WHEN increment_by < 0 AND max_value = -1
THEN NULL ELSE max_value END AS max_value, CASE WHEN increment_by > 0
AND min_value = 1 THEN NULL WHEN increment_by < 0 AND min_value =
-9223372036854775807 THEN NULL ELSE min_value END AS min_value,
cache_value, is_cycled FROM x

Toninght I'll review some parts of the code.

Regards,

Att,

--
Fabrízio de Royes Mello
Consultoria/Coaching PostgreSQL

Show quoted text

Timbira: http://www.timbira.com.br
Blog: http://fabriziomello.github.io
Linkedin: http://br.linkedin.com/in/fabriziomello
Twitter: http://twitter.com/fabriziomello
Github: http://github.com/fabriziomello

#111David Steele
david@pgmasters.net
In reply to: Fabrízio de Royes Mello (#110)
Re: Sequence Access Method WIP

Hi Petr,

On 3/28/16 3:11 PM, Fabr�zio de Royes Mello wrote:

fabrizio@bagual:~/pgsql
$ bin/pg_dump > /tmp/fabrizio.sql
pg_dump: [archiver (db)] query failed: ERROR: column "sequence_name"
does not exist
LINE 1: SELECT sequence_name, start_value, increment_by, CASE WHEN i...
^
pg_dump: [archiver (db)] query was: SELECT sequence_name, start_value,
increment_by, CASE WHEN increment_by > 0 AND max_value =
9223372036854775807 THEN NULL WHEN increment_by < 0 AND max_value =
-1 THEN NULL ELSE max_value END AS max_value, CASE WHEN
increment_by > 0 AND min_value = 1 THEN NULL WHEN increment_by < 0
AND min_value = -9223372036854775807 THEN NULL ELSE min_value END
AS min_value, cache_value, is_cycled FROM x

It looks like a new patch is needed. I've marked this "waiting on author".

Thanks,
--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#112Fabrízio de Royes Mello
fabriziomello@gmail.com
In reply to: David Steele (#111)
2 attachment(s)
Re: Sequence Access Method WIP

On Tue, Mar 29, 2016 at 12:25 PM, David Steele <david@pgmasters.net> wrote:

Hi Petr,

On 3/28/16 3:11 PM, Fabrízio de Royes Mello wrote:

fabrizio@bagual:~/pgsql
$ bin/pg_dump > /tmp/fabrizio.sql
pg_dump: [archiver (db)] query failed: ERROR: column "sequence_name"
does not exist
LINE 1: SELECT sequence_name, start_value, increment_by, CASE WHEN i...
^
pg_dump: [archiver (db)] query was: SELECT sequence_name, start_value,
increment_by, CASE WHEN increment_by > 0 AND max_value =
9223372036854775807 THEN NULL WHEN increment_by < 0 AND max_value =
-1 THEN NULL ELSE max_value END AS max_value, CASE WHEN
increment_by > 0 AND min_value = 1 THEN NULL WHEN increment_by < 0
AND min_value = -9223372036854775807 THEN NULL ELSE min_value END
AS min_value, cache_value, is_cycled FROM x

It looks like a new patch is needed. I've marked this "waiting on

author".

The attached patches fix the issues previous pointed by me.

But there are other issue in the gapless-seq extension when I ran "make
check":

43 CREATE EXTENSION gapless_seq;
44 CREATE SEQUENCE test_gapless USING gapless;
45 SELECT nextval('test_gapless'::regclass);
46 ! ERROR: could not access status of transaction 1275068416
47 ! DETAIL: Could not open file "pg_subtrans/4C00": No such file or
directory.
48 BEGIN;
49 SELECT nextval('test_gapless'::regclass);
50 ! ERROR: could not access status of transaction 1275068416
51 ! DETAIL: Could not open file "pg_subtrans/4C00": No such file or
directory.
52 SELECT nextval('test_gapless'::regclass);
53 ! ERROR: current transaction is aborted, commands ignored until end of
transaction block
54 SELECT nextval('test_gapless'::regclass);
55 ! ERROR: current transaction is aborted, commands ignored until end of
transaction block
56 ROLLBACK;
57 SELECT nextval('test_gapless'::regclass);
58 ! ERROR: could not access status of transaction 1275068416
59 ! DETAIL: Could not open file "pg_subtrans/4C00": No such file or
directory.

And I see the same running manually:

fabrizio=# create extension gapless_seq;
CREATE EXTENSION
fabrizio=# create sequence x using gapless;
CREATE SEQUENCE
fabrizio=# select nextval('x');
ERROR: could not access status of transaction 1258291200
DETAIL: Could not open file "pg_subtrans/4B00": No such file or directory.

Regards,

--
Fabrízio de Royes Mello
Consultoria/Coaching PostgreSQL

Show quoted text

Timbira: http://www.timbira.com.br
Blog: http://fabriziomello.github.io
Linkedin: http://br.linkedin.com/in/fabriziomello
Twitter: http://twitter.com/fabriziomello
Github: http://github.com/fabriziomello

Attachments:

0001-seqam-2016-03-29.patchtext/x-diff; charset=US-ASCII; name=0001-seqam-2016-03-29.patchDownload
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 30adece..fd355dc 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 7e82cdc..d37058a 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -251,6 +251,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
index 3c091f8..8676a84 100644
--- a/doc/src/sgml/ref/create_access_method.sgml
+++ b/doc/src/sgml/ref/create_access_method.sgml
@@ -61,7 +61,8 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
     <listitem>
      <para>
       This clause specifies type of access method to define.
-      Only <literal>INDEX</literal> is supported at present.
+      Supported values are <literal>INDEX</literal> for index access methods
+      and <literal>SEQUENCE</literal> for sequence access methods.
      </para>
     </listitem>
    </varlistentry>
@@ -75,7 +76,9 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
       of access method to the core.  The handler function must take single
       argument of type <type>internal</>, and its return type depends on the
       type of access method; for <literal>INDEX</literal> access methods, it
-      must be <type>index_am_handler</type>.
+      must be <type>index_am_handler</type> and for
+      <literal>SEQUENCE</literal> access methods it must be
+      <type>sequence_am_handler</type>.
      </para>
 
      <para>
@@ -114,6 +117,8 @@ CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;
    <member><xref linkend="sql-drop-access-method"></member>
    <member><xref linkend="sql-createopclass"></member>
    <member><xref linkend="sql-createopfamily"></member>
+   <member><xref linkend="index-api"> </member>
+   <member><xref linkend="seqam"></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..5faeae3
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,312 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_am</structname> system catalog (see
+   <xref linkend="catalog-pg-am">).
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The SQL interface of the sequence access method consists of a handler
+   function which has to return special pseudo-type
+   <type>sequence_handler</type>. This function must be written in a compiled
+   language such as C, using the version-1 interface. For details on C
+   language calling conventions and dynamic loading, see
+   <xref linkend="xfunc-c">.
+  </para>
+
+  <para>
+   The actual C structure returned by the handler function is following:
+<programlisting>
+typedef struct SeqAmRoutine
+{
+    NodeTag        type;
+
+    /* Custom datatype used for storing state of the sequence by the AM */
+    Oid            StateTypeOid;
+
+    /* Function for parsing reloptions */
+    ParseRelOptions_function    amoptions;
+
+    /* Initialization */
+    SeqAMInit_function          Init;
+
+    /* nextval handler */
+    SeqAMAlloc_function         Alloc;
+
+    /* State manipulation functions */
+    SeqAMSetval_function        Setval;
+    SeqAMGetState_function      GetState;
+    SeqAMSetState_function      SetState;
+} SeqAmRoutine;
+</programlisting>
+  </para>
+
+  <para>
+   These are the functions implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+Datum
+SeqAMInit (Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+           bool restart_requested, bool is_init)
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   The <literal>seq</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the Datum of a state of the sequence. The type
+   of the Datum is the <structname>StateTypeOid</structname> type.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+ParseRelOptions (Datum reloptions, bool validate)
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+SeqAMAlloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+            int64 *last)
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum SeqAMGetState (Relation seqrel, SequenceHandle *seqh)
+</programlisting>
+   Dump the current state of the sequence. The return value is the Datum of
+   a <structname>StateTypeOid</structname>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetState (Relation seqrel, SequenceHandle *seqh, Datum amstate)
+</programlisting>
+   Restore state of the sequence based on amstate which is Datum of a
+   <structname>StateTypeOid</structname> type.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following helper functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh)
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+Form_pg_sequence
+(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns the whole pg_sequence tuple as the
+   <structname>Form_pg_sequence</structname> structure.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum
+sequence_read_state(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns just the sequence access method specific state Datum.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_save_state(SequenceHandle *seqh, Datum seqstate, bool dowal)
+</programlisting>
+   Save the modified sequence state tuple indicating if the change should be
+   WAL logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh)
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_save_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh)
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_options</>
+   and/or added by <function>sequence_read_state</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_save_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors)
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname)
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a..223403d 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,7 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  tablesample transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc sequence \
+			  spgist tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ea0755a..bb368d2 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -888,8 +888,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
- * AM's options parser function in the case of a tuple corresponding to an
- * index, or NULL otherwise.
+ * or sequence AM's options parser function in the case of a tuple corresponding
+ * to an index or sequence respectively, or NULL otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
@@ -921,7 +921,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1374,14 +1375,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for access methods.
  *
- *	amoptions	index AM's option parser function
+ *	amoptions	AM's option parser function
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+am_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 {
 	Assert(amoptions != NULL);
 
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..1784799
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqamapi.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqamapi.c b/src/backend/access/sequence/seqamapi.c
new file mode 100644
index 0000000..bfdd589
--- /dev/null
+++ b/src/backend/access/sequence/seqamapi.c
@@ -0,0 +1,191 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqamapi.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amstate.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_am.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * GetSeqAmRoutine - call the specified access method handler routine
+ * to get its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutine(Oid amhandler)
+{
+	Datum			datum;
+	SeqAmRoutine   *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SeqAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SeqAmRoutine))
+		elog(ERROR, "sequence access method handler function %u did not return an SeqAmRoutine struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * GetSeqAmRoutineByAMId - look up the handler of the sequence access method
+ * for the given OID, and retrieve its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineByAMId(Oid amoid)
+{
+	HeapTuple		tuple;
+	regproc			amhandler;
+	Form_pg_am		amform;
+
+	/* 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);
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("sequence access method \"%s\" does not have a handler",
+						NameStr(amform->amname))));
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function. */
+	return GetSeqAmRoutine(amhandler);
+}
+
+/*
+ * GetSeqAmRoutineForRelation - look up the handler of the sequence access
+ * method for the given sequence Relation.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineForRelation(Relation relation)
+{
+	SeqAmRoutine *seqam;
+	SeqAmRoutine *cseqam;
+
+	if (relation->rd_seqamroutine == NULL)
+	{
+		if (!OidIsValid(relation->rd_rel->relam))
+		{
+			char *relname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(relation)),
+													   RelationGetRelationName(relation));
+			elog(ERROR, "relation %s isn't sequence", relname);
+		}
+
+		seqam = GetSeqAmRoutineByAMId(relation->rd_rel->relam);
+		/* Save the data for later reuse in CacheMemoryContext */
+		cseqam = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												   sizeof(SeqAmRoutine));
+		memcpy(cseqam, seqam, sizeof(SeqAmRoutine));
+		relation->rd_seqamroutine = cseqam;
+	}
+
+	return relation->rd_seqamroutine;
+}
+
+/*
+ * ValidateSeqAmRoutine - to sanity check for the SeqAmRoutine returned by a
+ * handler.
+ *
+ * At the moment we only check that the StateTypeOid is valid type oid and that
+ * the type it points to is fixed width and will fit into the sequence page.
+ */
+void
+ValidateSeqAmRoutine(SeqAmRoutine *routine)
+{
+#define MAX_SEQAM_STATE_LEN \
+	MaxHeapTupleSize - MinHeapTupleSize - MAXALIGN(offsetof(FormData_pg_sequence, amstate))
+
+	Oid	stattypeoid = routine->StateTypeOid;
+
+	if (!get_typisdefined(stattypeoid))
+		ereport(ERROR,
+				(errmsg("type with OID %u does not exist",
+						stattypeoid)));
+
+	if (TypeIsToastable(stattypeoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" must not be toastable",
+						format_type_be(stattypeoid))));
+
+	if (get_typlen(stattypeoid) >= MAX_SEQAM_STATE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" exceeds maximum allowed length (%lu)",
+						format_type_be(stattypeoid), MAX_SEQAM_STATE_LEN)));
+
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..2d31478
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,408 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+#include "utils/typcache.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+static bytea *seqam_local_reloptions(Datum reloptions, bool validate);
+static Datum seqam_local_init(Relation seqrel, Form_pg_sequence seq,
+							  int64 restart_value, bool restart_requested,
+							  bool is_init);
+static int64 seqam_local_alloc(Relation seqrel, SequenceHandle *seqh,
+							   int64 nrequested, int64 *last);
+static void seqam_local_setval(Relation seqrel, SequenceHandle *seqh,
+							   int64 new_value);
+static Datum seqam_local_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+								  Datum amstate);
+
+static char *
+seqam_local_state_get_part(StringInfo buf, char *ptr, bool *found)
+{
+	*found = false;
+
+	resetStringInfo(buf);
+
+	while (*ptr != ',' && *ptr != ')')
+	{
+		char		ch = *ptr++;
+
+		if (ch == '\0')
+		{
+			*found = false;
+			return ptr;
+		}
+
+		appendStringInfoChar(buf, ch);
+
+		if (!*found)
+			*found = true;
+	}
+
+	return ptr;
+}
+
+Datum
+seqam_local_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr;
+	Datum		d;
+	bool		found;
+	StringInfoData		buf;
+	LocalSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	initStringInfo(&buf);
+
+	/* last_value is required */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (!found)
+		goto malformed;
+	d = DirectFunctionCall1(int8in, CStringGetDatum(buf.data));
+	state->last_value = DatumGetInt64(d);
+	if (*ptr == ',')
+		ptr++;
+
+	/* is_called is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(boolin, CStringGetDatum(buf.data));
+		state->is_called = DatumGetBool(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->is_called = true;
+
+	/* log_cnt is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(int4in, CStringGetDatum(buf.data));
+		state->log_cnt = DatumGetInt32(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->log_cnt = 0;
+
+	if (*ptr != ')' || *(++ptr) != '\0')
+		goto malformed;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_local_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_local_state_out(PG_FUNCTION_ARGS)
+{
+	LocalSequenceState *state = (LocalSequenceState *) PG_GETARG_POINTER(0);
+	StringInfoData		buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s,%d)",
+					 state->last_value, state->is_called ? "t" : "f",
+					 state->log_cnt);
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the local sequence access method.
+ */
+Datum
+seqam_local_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine *seqam = makeNode(SeqAmRoutine);
+
+	seqam->StateTypeOid = SEQAMLOCALSTATEOID;
+	seqam->amoptions = seqam_local_reloptions;
+	seqam->Init = seqam_local_init;
+	seqam->Alloc = seqam_local_alloc;
+	seqam->Setval = seqam_local_setval;
+	seqam->GetState = seqam_local_get_state;
+	seqam->SetState = seqam_local_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+static bytea *
+seqam_local_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+static Datum
+seqam_local_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	LocalSequenceState	   *localseq;
+
+	if (is_init)
+		localseq = palloc0(sizeof(LocalSequenceState));
+	else
+		localseq = (LocalSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		localseq->last_value = restart_value;
+		localseq->is_called = false;
+	}
+
+	localseq->log_cnt = 0;
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+static int64
+seqam_local_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+				  int64 *last)
+{
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) seq->amstate;
+
+	next = result = localseq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = localseq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_start_update(seqh, logit);
+
+	if (logit)
+	{
+		localseq->last_value = next;
+		localseq->log_cnt = 0;
+		localseq->is_called = true;
+
+		sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	}
+
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_save_state(seqh, PointerGetDatum(localseq), false);
+
+	sequence_finish_update(seqh);
+
+	return result;
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+static void
+seqam_local_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq;
+
+	amstate = sequence_read_state(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	localseq->last_value = new_value;		/* last fetched number */
+	localseq->is_called = true;
+	localseq->log_cnt = 0;			/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+static Datum
+seqam_local_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq = palloc(sizeof(LocalSequenceState));
+
+	amstate = sequence_read_state(seqh);
+	memcpy(localseq, DatumGetPointer(amstate), sizeof(LocalSequenceState));
+	sequence_release_tuple(seqh);
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+static void
+seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+					  Datum amstate)
+{
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(localseq->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 41d2fd4..e684f60 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -251,6 +251,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e997b57..b7cd40a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -41,6 +41,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
@@ -88,6 +89,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -849,6 +851,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -922,6 +925,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1035,6 +1039,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1263,6 +1268,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1275,7 +1281,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the pg_am record.
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1330,6 +1337,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = AccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index cb3ba85..b40f0c7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index f40a005..40b5e91 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -291,6 +291,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 68ea5f3..e7bfa96 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -15,6 +15,8 @@
 
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_am.h"
@@ -28,11 +30,9 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
-
-static Oid	lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid	lookup_am_handler_func(List *handler_name, char amtype);
 static const char *get_am_type_string(char amtype);
 
-
 /*
  * CreateAcessMethod
  *		Registers a new access method.
@@ -72,7 +72,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	/*
 	 * Get the handler function oid, verifying the AM type while at it.
 	 */
-	amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+	amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
 
 	/*
 	 * Insert tuple into pg_am.
@@ -187,6 +187,16 @@ get_index_am_oid(const char *amname, bool missing_ok)
 }
 
 /*
+ * get_seq_am_oid - given an access method name, look up its OID
+ *		and verify it corresponds to an sequence AM.
+ */
+Oid
+get_seq_am_oid(const char *amname, bool missing_ok)
+{
+	return get_am_type_oid(amname, AMTYPE_SEQUENCE, missing_ok);
+}
+
+/*
  * get_am_oid - given an access method name, look up its OID.
  * 		The type is not checked.
  */
@@ -226,6 +236,8 @@ get_am_type_string(char amtype)
 	{
 		case AMTYPE_INDEX:
 			return "INDEX";
+		case AMTYPE_SEQUENCE:
+			return "SEQUENCE";
 		default:
 			/* shouldn't happen */
 			elog(ERROR, "invalid access method type '%c'", amtype);
@@ -240,9 +252,10 @@ get_am_type_string(char amtype)
  * This function either return valid function Oid or throw an error.
  */
 static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
 {
 	Oid			handlerOid;
+	Oid			handler_type;
 	static const Oid funcargtypes[1] = {INTERNALOID};
 
 	if (handler_name == NIL)
@@ -257,16 +270,21 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
 	switch (amtype)
 	{
 		case AMTYPE_INDEX:
-			if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
-				ereport(ERROR,
-						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-						 errmsg("function %s must return type \"%s\"",
-								NameListToString(handler_name),
-								"index_am_handler")));
+			handler_type = INDEX_AM_HANDLEROID;
+			break;
+		case AMTYPE_SEQUENCE:
+			handler_type = SEQ_AM_HANDLEROID;
 			break;
 		default:
 			elog(ERROR, "unrecognized access method type \"%c\"", amtype);
 	}
 
+	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;
 }
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5cb28cf..54a22da 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -680,6 +680,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cb7a145..ab7b7a3 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 13b04e6..92ae4db 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -536,7 +536,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..ad4159b 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,16 +14,22 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/tupmacs.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +42,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,24 +82,112 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	Oid			statetyp;
+	int16		statetyplen;
+	bool		statetypbyval;
+	HeapTupleData tup;
+	bool		inupdate;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
  */
 static SeqTableData *last_used_seq = NULL;
 
+static HeapTuple build_seq_tuple(Relation rel, SeqAmRoutine *seqam,
+								 Form_pg_sequence new, int64 restart_value);
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+
+/*
+ * Build template column definition for a sequence relation.
+*/
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_not_null = true;
+	coldef->is_from_type = false;
+	/* Force plain storage. */
+	coldef->storage = 'p';
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(Oid amstateTypeOid)
+{
+	List	   *seqcols;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		switch (colid)
+		{
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+			case SEQ_COL_AMSTATE:
+				coldef->typeName = makeTypeNameFromOid(amstateTypeOid, -1);
+				coldef->colname = "amstate";
+				break;
+		}
+
+		seqcols = lappend(seqcols, coldef);
+	}
 
+	return seqcols;
+}
 
 /*
  * DefineSequence
@@ -111,14 +200,12 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
+	List	   *seqcols;
+	SeqAmRoutine *seqam;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -147,102 +234,33 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
-	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
-
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
-		{
-			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
-				break;
-			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
-				break;
-			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
-				break;
-			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
-				break;
-			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
-				break;
-			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
-			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
-				break;
-		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
-	}
+	if (seq->accessMethod)
+		seqamid = get_seq_am_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
+	seqam = GetSeqAmRoutineByAMId(seqamid);
+	/* Build column definitions. */
+	seqcols = BuildSeqColumnDefList(seqam->StateTypeOid);
 
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
+	stmt->tableElts = seqcols;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	/* Build new sequence tuple and store it. */
+	tuple = build_seq_tuple(rel, seqam, &new, new.start_value);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +272,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +286,89 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
+	TupleDesc			tupDesc;
+	Datum	   values[SEQ_COL_LASTCOL];
+	bool	   nulls[SEQ_COL_LASTCOL];
+	SeqAmRoutine *seqam;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
-	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tupDesc = RelationGetDescr(seqrel);
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(seqrel, seq, seq->start_value,
+											  true, false);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
+}
+
+/*
+ * Build sequence tuple based on the sequence form and fill in the
+ * sequence AM specific info as well.
+ */
+static HeapTuple
+build_seq_tuple(Relation rel, SeqAmRoutine *seqam, Form_pg_sequence new,
+				int64 restart_value)
+{
+	TupleDesc	tupDesc;
+	HeapTuple	tuple;
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+
+	tupDesc = RelationGetDescr(rel);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new->start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new->increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new->max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new->min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new->cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new->is_cycled);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(rel, new, restart_value,
+											  false, true);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	return tuple;
 }
 
 /*
@@ -361,7 +411,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +431,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,6 +439,69 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 }
 
 /*
+ * Replace the type of amstate column.
+ *
+ * We don't do AlterTable here as that produces dead columns which we don't
+ * want. This is safe because the sequence page is controlled by code in this
+ * module and isn't changed the same way as a table.
+ *
+ * TODO: check if anybody is depending on the row-type associated with the
+ * sequence.
+ */
+static void
+replace_sequence_amstate_col(Oid seqrelid, Oid typid)
+{
+	Relation	attr_rel;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	HeapTuple	tp,
+				attr_tuple,
+				newattr_tuple;
+	Form_pg_type	typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+
+	memset(nulls, 0, sizeof(nulls));
+	memset(replace, 0, sizeof(replace));
+
+	replace[Anum_pg_attribute_atttypid - 1] = true;
+	replace[Anum_pg_attribute_attlen - 1] = true;
+	replace[Anum_pg_attribute_attbyval - 1] = true;
+	replace[Anum_pg_attribute_attalign - 1] = true;
+
+	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(typid);
+	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(typtup->typlen);
+	values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(typtup->typbyval);
+	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(typtup->typalign);
+
+	/* Build DROP command for amstate of old AM. */
+	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+	attr_tuple = SearchSysCache2(ATTNUM,
+								 ObjectIdGetDatum(seqrelid),
+								 Int16GetDatum(SEQ_COL_AMSTATE));
+	if (!HeapTupleIsValid(attr_tuple))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 SEQ_COL_AMSTATE, seqrelid);
+
+	newattr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel),
+									  values, nulls, replace);
+	simple_heap_update(attr_rel, &newattr_tuple->t_self, newattr_tuple);
+	CatalogUpdateIndexes(attr_rel, newattr_tuple);
+
+	ReleaseSysCache(tp);
+	heap_freetuple(newattr_tuple);
+	ReleaseSysCache(attr_tuple);
+
+	heap_close(attr_rel, RowExclusiveLock);
+}
+
+/*
  * AlterSequence
  *
  * Modify the definition of a sequence relation
@@ -406,19 +509,24 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence seq,
+					 new;
 	List	   *owned_by;
 	ObjectAddress address;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
+	SeqAmRoutine *oldseqam;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +534,187 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
+	oldamid = seqrel->rd_rel->relam;
+	oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	if (stmt->accessMethod)
+		seqamid = get_seq_am_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(stmt->options, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		HeapTuple       seqamtup;
+		Form_pg_am		form_am;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
+		SeqAmRoutine	   *newseqam;
+
+		oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = oldseqam->Alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
+
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(AMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
+
+		newseqam = GetSeqAmRoutineByAMId(seqamid);
+
+		form_am = (Form_pg_am) GETSTRUCT(seqamtup);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		validnsps[0] = NameStr(form_am->amname);
+		validnsps[1] = NULL;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		(void) am_reloptions(newseqam->amoptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-		PageSetLSN(page, recptr);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
+
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
+
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * And change the type definition.
+		 *
+		 * We can't use AlterTable internals here because the sequence
+		 * has to have the expected number of columns and no
+		 * attisdropped = true columns.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		replace_sequence_amstate_col(seqrelid, newseqam->StateTypeOid);
+		CommandCounterIncrement();
+
+		/* Rebuild the sequence tuple and save it. */
+		tuple = build_seq_tuple(seqrel, newseqam, new, restart_value);
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										AccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = AccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
+	else
+	{
+		Datum	   newamstate;
 
-	END_CRIT_SECTION();
+		sequence_check_range(restart_value, new->min_value, new->max_value,
+							 restart_requested ? "RESTART" : "START");
 
-	UnlockReleaseBuffer(buf);
+		/* Let the new sequence AM initialize. */
+		newamstate = oldseqam->Init(seqrel, new, restart_value,
+									restart_requested, false);
+
+		sequence_start_update(&seqh, true);
+		memcpy(seq, new, offsetof(FormData_pg_sequence, amstate));
+		sequence_save_state(&seqh, newamstate, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +755,26 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +799,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam->Alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +816,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for syncrep.
-	 * It's sufficient to ensure the toplevel transaction has an xid, no need
-	 * to assign xids subxacts, that'll already trigger an appropriate wait.
-	 * (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +827,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +888,26 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,106 +924,86 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam->Setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	last_used_seq = elm;
 
-	UnlockReleaseBuffer(buf);
+	sequence_close(&seqh);
 
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	LocalSequenceState	state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
+
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-	do_setval(relid, next, true);
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Build the state and pass it to sequence AM. */
+	state.last_value = next;
+	state.log_cnt = 0;
+	state.is_called = iscalled;
+	seqam->SetState(seqh.rel, &seqh, PointerGetDatum(&state));
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-	PG_RETURN_INT64(next);
-}
+	/* Reset local cached data */
+	elm->cached = elm->last;
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1034,11 +1062,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1117,58 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->statetyp = 6025; /* TODO */
+	seqh->statetyplen = -1;
+	seqh->statetypbyval = false;
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(!seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1178,170 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+Form_pg_sequence
+sequence_read_options(SequenceHandle *seqh)
+{
+	return (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
+}
+
+Datum
+sequence_read_state(SequenceHandle *seqh)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+
+	return PointerGetDatum(seq->amstate);
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool dowal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+
+	if (seqh->statetyplen < 0)
+	{
+		get_typlenbyval(seqh->statetyp, &seqh->statetyplen,
+						&seqh->statetypbyval);
+		Assert(seqh->statetyplen > 0);
+	}
+
+	if (dowal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_save_state(SequenceHandle *seqh, Datum amstate, bool dowal)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+	Page				page;
+
+	/*
+	 * Update the state data inline.
+	 *
+	 * This is only needed when the provided amstate datum points to different
+	 * data than what is already in the tuple.
+	 */
+	if (DatumGetPointer(amstate) != seq->amstate)
+	{
+		if (seqh->statetypbyval)
+			store_att_byval(seq->amstate, amstate, seqh->statetyplen);
+		else
+			memmove(seq->amstate, DatumGetPointer(amstate), seqh->statetyplen);
+	}
+
+	page = BufferGetPage(seqh->buf);
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (dowal && RelationNeedsWAL(seqh->rel))
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	Assert(BufferIsValid(seqh->buf));
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1351,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1428,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1436,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1445,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1453,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1460,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1473,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1526,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1540,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1650,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1678,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1686,85 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Datum		state;
+	char	   *statestr;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typoutput;
+	bool		typisvarlena;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type output function. */
+	getTypeOutputInfo(seqam->StateTypeOid, &typoutput, &typisvarlena);
+
+	/* Get the output and convert it to string. */
+	state = seqam->GetState(seqh.rel, &seqh);
+	statestr = OidOutputFunctionCall(typoutput, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_TEXT_P(cstring_to_text(statestr));
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *statestr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typinput,
+				typioparam;
+	Datum		state;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type input function. */
+	getTypeInputInfo(seqam->StateTypeOid, &typinput, &typioparam);
+
+	/* Convert the string to the state type and set it as new state. */
+	state = OidInputFunctionCall(typinput, statestr, typioparam, -1);
+	seqam->SetState(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1830,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 96dc923..6e6ee5d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -269,6 +270,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -454,7 +456,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -473,7 +475,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -551,13 +552,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		SeqAmRoutine *seqam;
+
+		Assert(relamid != InvalidOid);
+		seqam = GetSeqAmRoutineByAMId(relamid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 NULL, true, false);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		(void) am_reloptions(seqam->amoptions, reloptions, true);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -678,6 +695,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3308,7 +3326,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW |
+								ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4308,6 +4327,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4351,8 +4373,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a table, view, or foreign table");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9403,12 +9425,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqamroutine->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
+					 errmsg("\"%s\" is not a table, view, materialized view, index, sequence, or TOAST table",
 							RelationGetRelationName(rel))));
 			break;
 	}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 3de29b7..7aab5ab 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2116,7 +2116,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e9d9ba2..ca0f481 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6378db8..8c19b31 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3564,7 +3564,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3577,7 +3579,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 854c062..5972ca6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1627,7 +1627,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1638,7 +1640,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1273352..75af985 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -310,7 +310,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <list>	event_trigger_when_list event_trigger_value_list
 %type <defelt>	event_trigger_when_item
-%type <chr>		enable_trigger
+%type <chr>		enable_trigger am_type
 
 %type <str>		copy_file_name
 				database_name access_method_clause access_method attr_name
@@ -605,7 +605,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 METHOD 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
@@ -3587,7 +3588,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3609,7 +3636,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3617,11 +3668,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3680,7 +3754,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
@@ -4715,16 +4789,21 @@ row_security_cmd:
  *
  *****************************************************************************/
 
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
 				{
 					CreateAmStmt *n = makeNode(CreateAmStmt);
 					n->amname = $4;
 					n->handler_name = $8;
-					n->amtype = AMTYPE_INDEX;
+					n->amtype = $6;
 					$$ = (Node *) n;
 				}
 		;
 
+am_type:
+			INDEX		{ $$ = AMTYPE_INDEX; }
+		|	SEQUENCE	{ $$ = AMTYPE_SEQUENCE; }
+		;
+
 /*****************************************************************************
  *
  *		QUERIES :
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6528494..509fa32 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -29,6 +29,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -456,6 +457,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4d0aac9..8b95577 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -965,7 +965,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -998,7 +999,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index dd447cf..6c1456e 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -399,6 +399,31 @@ index_am_handler_out(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();			/* keep compiler quiet */
 }
 
+/*
+ * seq_am_handler_int		- input routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_in(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * seq_am_handler_out		- output routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_out(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot display a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
 
 /*
  * tsm_handler_in		- input routine for pseudo-type TSM_HANDLER.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 130c06d..b9686da 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -260,6 +261,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -424,6 +426,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	amoptions_function	amoptions = NULL;
 
 	relation->rd_options = NULL;
 
@@ -432,10 +435,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_amroutine->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqamroutine->amoptions;
+			break;
 		default:
 			return;
 	}
@@ -447,8 +455,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_amroutine->amoptions : NULL);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1049,11 +1056,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1555,6 +1565,34 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation relation)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	SeqAmRoutine	   *result, *tmp;
+
+	/*
+	 * Look up the index's access method, save the OID of its handler function
+	 */
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 relation->rd_rel->relam);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amtype == AMTYPE_SEQUENCE);
+	relation->rd_amhandler = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	result = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												 sizeof(SeqAmRoutine));
+	tmp = GetSeqAmRoutine(relation->rd_amhandler);
+	memcpy(result, tmp, sizeof(SeqAmRoutine));
+	relation->rd_seqamroutine = result;
+}
 
 /*
  *		formrdesc
@@ -4885,6 +4923,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqamroutine == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 65a6cd4..074406a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqamapi.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 077b37e..45778fd 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4747,6 +4747,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4798,6 +4799,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4840,6 +4842,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4882,6 +4885,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4924,6 +4928,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4964,6 +4969,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5003,6 +5009,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5042,6 +5049,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5081,6 +5089,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5119,6 +5128,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5153,6 +5163,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5182,6 +5193,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5221,6 +5233,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5274,6 +5287,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5339,6 +5353,10 @@ getTables(Archive *fout, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -11577,6 +11595,9 @@ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
 		case AMTYPE_INDEX:
 			appendPQExpBuffer(q, "TYPE INDEX ");
 			break;
+		case AMTYPE_SEQUENCE:
+			appendPQExpBuffer(q, "TYPE SEQUENCE ");
+			break;
 		default:
 			write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
 					  aminfo->amtype, qamname);
@@ -15281,7 +15302,8 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -15361,6 +15383,37 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
 	/*
+	 * 9.6 adds sequence access methods but we only care if valid
+	 * sequence am that is not the default one is specified.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		PGresult   *res2;
+
+		printfPQExpBuffer(query, "SELECT a.amname\n"
+								 "FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
+
+	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
 	 */
 	appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -15401,6 +15454,13 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -15467,6 +15527,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15483,16 +15546,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/*
+	 * On 9.6 there is special interface for dumping sequences but we only
+	 * use it for one with nondefault access method because we can produce
+	 * more backward compatible dump that way.
+	 */
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15505,14 +15581,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90600 &&
+		tbinfo->relam != InvalidOid &&
+		tbinfo->relam != LOCAL_SEQAM_OID)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index c02c536..c62abcc 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -231,6 +231,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_class) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 7b2f4e6..ac85da3 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1403,30 +1403,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1470,13 +1446,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1805,6 +1823,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1814,12 +1834,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.amname\n"
+								"FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 469ac67..69135aa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -273,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
+extern bytea *am_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqamapi.h b/src/include/access/seqamapi.h
new file mode 100644
index 0000000..a4c826a
--- /dev/null
+++ b/src/include/access/seqamapi.h
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqamapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAMAPI_H
+#define SEQAMAPI_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef bytea * (*ParseRelOptions_function) (Datum reloptions,
+											 bool validate);
+
+typedef Datum (*SeqAMInit_function) (Relation seqrel,
+									 Form_pg_sequence seq,
+									 int64 restart_value,
+									 bool restart_requested,
+									 bool is_init);
+
+typedef int64 (*SeqAMAlloc_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 nrequested,
+									  int64 *last);
+
+typedef void (*SeqAMSetval_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 new_value);
+
+typedef Datum (*SeqAMGetState_function) (Relation seqrel,
+										 SequenceHandle *seqh);
+typedef void (*SeqAMSetState_function) (Relation seqrel, SequenceHandle *seqh,
+										Datum amstate);
+
+typedef struct SeqAmRoutine
+{
+	NodeTag		type;
+
+	/* Custom columns needed by the AM */
+	Oid			StateTypeOid;
+
+	/* Function for parsing reloptions */
+	ParseRelOptions_function	amoptions;
+
+	/* Initialization */
+	SeqAMInit_function			Init;
+
+	/* nextval handler */
+	SeqAMAlloc_function			Alloc;
+
+	/* State manipulation functions */
+	SeqAMSetval_function		Setval;
+	SeqAMGetState_function		GetState;
+	SeqAMSetState_function		SetState;
+} SeqAmRoutine;
+
+extern SeqAmRoutine *GetSeqAmRoutine(Oid seqamhandler);
+extern SeqAmRoutine *GetSeqAmRoutineByAMId(Oid amoid);
+extern SeqAmRoutine *GetSeqAmRoutineForRelation(Relation relation);
+extern void ValidateSeqAmRoutine(SeqAmRoutine *routine);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern Form_pg_sequence sequence_read_options(SequenceHandle *seqh);
+extern Datum sequence_read_state(SequenceHandle *seqh);
+extern void sequence_start_update(SequenceHandle *seqh, bool dowal);
+extern void sequence_save_state(SequenceHandle *seqh, Datum seqstate,
+								bool dowal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+/* We need this public for serval3 */
+typedef struct LocalSequenceState
+{
+	int64   last_value;
+	int32	log_cnt;
+	bool	is_called;
+} LocalSequenceState;
+
+extern Datum seqam_local_state_in(PG_FUNCTION_ARGS);
+extern Datum seqam_local_state_out(PG_FUNCTION_ARGS);
+extern Datum seqam_local_handler(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAMAPI_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..d6b3cd7 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 1116923..9c615f7 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
  * ----------------
  */
 #define AMTYPE_INDEX					'i'		/* index access method */
+#define	AMTYPE_SEQUENCE					'S'		/* sequence access method */
 
 /* ----------------
  *		initial contents of pg_am
@@ -84,4 +85,8 @@ DATA(insert OID = 3580 (  brin		brinhandler i ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
+DATA(insert OID = 6023 (  local		seqam_local_handler	S ));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6023
+
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a595327..c8f86f2 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1759,6 +1759,10 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v u 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 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 25 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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 s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3690,6 +3694,10 @@ DATA(insert OID = 326  (  index_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f
 DESCR("I/O");
 DATA(insert OID = 327  (  index_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
 DESCR("I/O");
+DATA(insert OID = 6021  (  seq_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 6020 "2275" _null_ _null_ _null_ _null_ _null_ seq_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6022 (  seq_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6020" _null_ _null_ _null_ _null_ _null_ seq_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
 DATA(insert OID = 3311 (  tsm_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 3312 (  tsm_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
@@ -3701,6 +3709,10 @@ DESCR("BERNOULLI tablesample method handler");
 DATA(insert OID = 3314 (  system			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_system_handler _null_ _null_ _null_ ));
 DESCR("SYSTEM tablesample method handler");
 
+/* sequence access method handlers */
+DATA(insert OID = 6024 (  seqam_local_handler	   PGNSP PGUID 12 1 0 0 0 f f f f f f v s 1 0 6020 "2281" _null_ _null_ _null_ _null_ _null_ seqam_local_handler _null_ _null_ _null_ ));
+DESCR("Local SequenceAM handler");
+
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
 DESCR("MD5 hash");
@@ -5259,6 +5271,13 @@ DESCR("pg_controldata recovery state information as a function");
 DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
 DESCR("pg_controldata init state information as a function");
 
+/* Sequence AM */
+DATA(insert OID = 6027 (  seqam_local_state_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 6025 "2275" _null_ _null_ _null_ _null_ _null_ seqam_local_state_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6028 (  seqam_local_state_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6025" _null_ _null_ _null_ _null_ _null_ seqam_local_state_out _null_ _null_ _null_ ));
+DESCR("I/O");
+
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2c90b76..40214e3 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -696,11 +696,18 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 #define FDW_HANDLEROID	3115
 DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define INDEX_AM_HANDLEROID	325
+DATA(insert OID = 6020 ( seq_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 seq_am_handler_in seq_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQ_AM_HANDLEROID	6020
 DATA(insert OID = 3310 ( tsm_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define TSM_HANDLEROID	3310
 DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 #define ANYRANGEOID		3831
 
+/* Sequence AM composite types */
+DATA(insert OID = 6025 ( seqam_local_state	PGNSP PGUID 16 f b U f t \054 0 0 6026 seqam_local_state_in seqam_local_state_out - - - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQAMLOCALSTATEOID 6025
+DATA(insert OID = 6026 ( _seqam_local_state	PGNSP PGUID -1 f b A f t \054 0 6025 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 
 /*
  * macros
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index b064eb4..c9bf431 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -139,6 +139,7 @@ extern Datum transformGenericOptions(Oid catalogId,
 extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
 extern void RemoveAccessMethodById(Oid amOid);
 extern Oid	get_index_am_oid(const char *amname, bool missing_ok);
+extern Oid	get_seq_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);
 
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..e0f6439 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -19,20 +19,18 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "storage/relfilenode.h"
-
+#include "access/htup_details.h"
 
 typedef struct FormData_pg_sequence
 {
-	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
+	/* amstate follows */
+	char		amstate[FLEXIBLE_ARRAY_MEMBER];
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,19 +39,16 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  * Columns of a sequence relation
  */
 
-#define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_STARTVAL		1
+#define SEQ_COL_INCBY			2
+#define SEQ_COL_MAXVALUE		3
+#define SEQ_COL_MINVALUE		4
+#define SEQ_COL_CACHE			5
+#define SEQ_COL_CYCLE			6
+#define SEQ_COL_AMSTATE			7
 
-#define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_FIRSTCOL		SEQ_COL_STARTVAL
+#define SEQ_COL_LASTCOL			SEQ_COL_AMSTATE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +67,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 7a770f4..1bceec8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 734df77..752d3a1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -477,6 +477,7 @@ typedef enum NodeTag
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
+	T_SeqAmRoutine,				/* in access/seqamapi.h */
 	T_TsmRoutine				/* in access/tsmapi.h */
 } NodeTag;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8b958b4..859fbad 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2205,8 +2205,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2214,8 +2216,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 206288d..1c89b49 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -584,6 +584,8 @@ extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
 extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_in(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_out(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
 extern Datum internal_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f2bebf2..4fe7e52 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -106,13 +106,18 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	Oid			rd_amhandler;	/* OID of access method's handler function */
+
+	/* These are non-NULL only for a sequence relation */
+	struct SeqAmRoutine *rd_seqamroutine;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
@@ -126,7 +131,6 @@ typedef struct RelationData
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
-	Oid			rd_amhandler;	/* OID of index AM's handler function */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
 	/* use "struct" here to avoid needing to include amapi.h: */
 	struct IndexAmRoutine *rd_amroutine;		/* index AM's API struct */
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index 47d6024..9242982 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -106,3 +106,22 @@ drop cascades to index grect2ind
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+-- Checks for sequence access methods
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+ERROR:  cannot drop access method local2 because other objects depend on it
+DETAIL:  sequence test_seqam depends on access method local2
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ae50c94..f1b1fc2 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -129,8 +129,9 @@ NOTICE:  view "v12_temp" will be a temporary view
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 NOTICE:  view "v13_temp" will be a temporary view
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 7c09fa3..ab0d299 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1586,7 +1586,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
  oid | amname | oid | proname 
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3a19cde 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate 
+-------------+--------------+---------------------+-----------+-------------+-----------+---------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (1,f,0)
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate  
+-------------+--------------+---------------------+-----------+-------------+-----------+----------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (2,t,31)
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c5dfbb5..a30582e 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,52 @@ SELECT table_name, column_name, is_updatable
   FROM information_schema.columns
  WHERE table_name LIKE E'r_\\_view%'
  ORDER BY table_name, ordinal_position;
- table_name |  column_name  | is_updatable 
-------------+---------------+--------------
- ro_view1   | a             | NO
- ro_view1   | b             | NO
- ro_view10  | a             | NO
- ro_view11  | a             | NO
- ro_view11  | b             | NO
- ro_view12  | a             | NO
- ro_view13  | a             | NO
- ro_view13  | b             | NO
- ro_view17  | a             | NO
- ro_view17  | b             | NO
- ro_view18  | a             | NO
- ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
- ro_view19  | start_value   | NO
- ro_view19  | increment_by  | NO
- ro_view19  | max_value     | NO
- ro_view19  | min_value     | NO
- ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
- ro_view19  | is_cycled     | NO
- ro_view19  | is_called     | NO
- ro_view2   | a             | NO
- ro_view2   | b             | NO
- ro_view20  | a             | NO
- ro_view20  | b             | NO
- ro_view20  | g             | NO
- ro_view3   | ?column?      | NO
- ro_view4   | count         | NO
- ro_view5   | a             | NO
- ro_view5   | rank          | NO
- ro_view6   | a             | NO
- ro_view6   | b             | NO
- ro_view7   | a             | NO
- ro_view7   | b             | NO
- ro_view8   | a             | NO
- ro_view8   | b             | NO
- ro_view9   | a             | NO
- ro_view9   | b             | NO
- rw_view14  | ctid          | NO
- rw_view14  | a             | YES
- rw_view14  | b             | YES
- rw_view15  | a             | YES
- rw_view15  | upper         | NO
- rw_view16  | a             | YES
- rw_view16  | b             | YES
- rw_view16  | aa            | YES
-(46 rows)
+ table_name | column_name  | is_updatable 
+------------+--------------+--------------
+ ro_view1   | a            | NO
+ ro_view1   | b            | NO
+ ro_view10  | a            | NO
+ ro_view11  | a            | NO
+ ro_view11  | b            | NO
+ ro_view12  | a            | NO
+ ro_view13  | a            | NO
+ ro_view13  | b            | NO
+ ro_view17  | a            | NO
+ ro_view17  | b            | NO
+ ro_view18  | a            | NO
+ ro_view19  | start_value  | NO
+ ro_view19  | increment_by | NO
+ ro_view19  | max_value    | NO
+ ro_view19  | min_value    | NO
+ ro_view19  | cache_value  | NO
+ ro_view19  | is_cycled    | NO
+ ro_view19  | amstate      | NO
+ ro_view2   | a            | NO
+ ro_view2   | b            | NO
+ ro_view20  | a            | NO
+ ro_view20  | b            | NO
+ ro_view20  | g            | NO
+ ro_view3   | ?column?     | NO
+ ro_view4   | count        | NO
+ ro_view5   | a            | NO
+ ro_view5   | rank         | NO
+ ro_view6   | a            | NO
+ ro_view6   | b            | NO
+ ro_view7   | a            | NO
+ ro_view7   | b            | NO
+ ro_view8   | a            | NO
+ ro_view8   | b            | NO
+ ro_view9   | a            | NO
+ ro_view9   | b            | NO
+ rw_view14  | ctid         | NO
+ rw_view14  | a            | YES
+ rw_view14  | b            | YES
+ rw_view15  | a            | YES
+ rw_view15  | upper        | NO
+ rw_view16  | a            | YES
+ rw_view16  | b            | YES
+ rw_view16  | aa           | YES
+(43 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index e2051c5..e811ea3 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -71,3 +71,19 @@ DROP ACCESS METHOD gist2 CASCADE;
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+
+-- Checks for sequence access methods
+
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 58d361d..e967fff 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -131,8 +131,9 @@ CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 6c9784a..6c44d70 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1055,7 +1055,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
 
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..c2b1164 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
0002-gapless-seq-2016-03-29.patchtext/x-diff; charset=US-ASCII; name=0002-gapless-seq-2016-03-29.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
index d12dd63..d2a0620 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
 		earthdistance	\
 		file_fdw	\
 		fuzzystrmatch	\
+		gapless_seq	\
 		hstore		\
 		intagg		\
 		intarray	\
diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..130104a
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+ERROR:  cannot drop access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..3b41f86
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,57 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_handler(internal)
+RETURNS SEQ_AM_HANDLER
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_in(cstring)
+RETURNS seqam_gapless_state
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_out(seqam_gapless_state)
+RETURNS cstring
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TYPE seqam_gapless_state (
+	INPUT   = seqam_gapless_state_in,
+	OUTPUT  = seqam_gapless_state_out,
+	INTERNALLENGTH = 16
+);
+COMMENT ON TYPE seqam_gapless_state IS 'state for gapless sequence am';
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	last_value bigint NOT NULL,
+	is_called bool NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relkind = 'S' AND relam = (
+			SELECT oid FROM pg_am WHERE amname = 'gapless' AND amtype = 'S'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD gapless TYPE SEQUENCE HANDLER seqam_gapless_handler;
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..57f1570
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,531 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager / Gapless sequence implementation
+ *
+ *------------------------------------------------------------
+ */
+
+typedef struct GaplessSequenceState
+{
+	int64   last_value;
+	uint32	xid;
+	bool	is_called;
+} GaplessSequenceState;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	int64	last_value;
+	bool	is_called;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static bytea *seqam_gapless_reloptions(Datum reloptions, bool validate);
+static Datum seqam_gapless_init(Relation seqrel, Form_pg_sequence seq,
+								int64 restart_value, bool restart_requested,
+								bool is_init);
+static int64 seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh,
+								 int64 nrequested, int64 *last);
+static void seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh,
+								 int64 new_value);
+static Datum seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+									Datum amstate);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_handler);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_in);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_out);
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+							   int64 last_value, bool is_called);
+
+Datum
+seqam_gapless_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr, *end;
+	Datum		d;
+	GaplessSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	end = ptr;
+	while (*end != ',' && *end != ')')
+	{
+		char		ch = *end++;
+
+		if (ch == '\0')
+			goto malformed;
+	}
+
+	d = DirectFunctionCall1(int8in,
+							CStringGetDatum(pnstrdup(ptr, end-ptr)));
+	state->last_value = DatumGetInt64(d);
+
+	/* is_called is optional */
+	if (*end == ',')
+	{
+		ptr = ++end;
+
+		end = state_str+strlen(state_str)-1;
+
+		if (*end != ')')
+			goto malformed;
+
+		d = DirectFunctionCall1(boolin,
+								CStringGetDatum(pnstrdup(ptr, end-ptr)));
+		state->is_called = DatumGetBool(d);
+	}
+	else
+		state->is_called = true;
+
+	state->xid = InvalidTransactionId;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_gapless_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_gapless_state_out(PG_FUNCTION_ARGS)
+{
+	GaplessSequenceState   *state;
+	StringInfoData			buf;
+
+	state = (GaplessSequenceState *) PG_GETARG_POINTER(0);
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s)",
+					 state->last_value, state->is_called ? "t" : "f");
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the gapless sequence access method.
+ */
+Datum
+seqam_gapless_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine   *seqam = makeNode(SeqAmRoutine);
+	Oid				nspid,
+					typid;
+
+	nspid = get_namespace_oid("gapless_seq", false);
+	typid = GetSysCacheOid2(TYPENAMENSP,
+							PointerGetDatum("seqam_gapless_state"),
+							ObjectIdGetDatum(nspid));
+
+	if (!OidIsValid(typid))
+		elog(ERROR, "cache lookup failed for type \"seqam_gapless_state\"");
+
+	seqam->StateTypeOid = typid;
+	seqam->amoptions = seqam_gapless_reloptions;
+	seqam->Init = seqam_gapless_init;
+	seqam->Alloc = seqam_gapless_alloc;
+	seqam->Setval = seqam_gapless_setval;
+	seqam->GetState = seqam_gapless_get_state;
+	seqam->SetState = seqam_gapless_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+static bytea *
+seqam_gapless_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+static Datum
+seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	Oid			seqrelid = seqrel->rd_id;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	GaplessSequenceState   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(GaplessSequenceState));
+	else
+		seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->xid = GetTopTransactionId();
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, valtuple, seqstate->last_value,
+						   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+static int64
+seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+					int64 *last)
+{
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	Form_pg_sequence		seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, result, true);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* We always WAL log for gapless sequence. */
+	sequence_start_update(seqh, true);
+	seqstate->last_value = result;
+	seqstate->is_called = true;
+	if (seqstate->xid != local_xid)
+		seqstate->xid = local_xid;
+	sequence_save_state(seqh, PointerGetDatum(seqstate), true);
+	sequence_finish_update(seqh);
+
+	*last = result;
+
+	return result;
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+static void
+seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+static Datum
+seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	GaplessSequenceState   *seqstate = palloc0(sizeof(GaplessSequenceState));
+
+	/*
+	 * Get the last value from the values table, if not found use the values
+	 * from sequence typle.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		seqstate->last_value = v->last_value;
+		seqstate->is_called = v->is_called;
+	}
+	else
+	{
+		Datum		amstate = sequence_read_state(seqh);
+
+		memcpy(seqstate, DatumGetPointer(amstate),
+			   sizeof(GaplessSequenceState));
+		sequence_release_tuple(seqh);
+	}
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+static void
+seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+						Datum amstate)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	FormData_pg_sequence   *seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(seqstate->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, seqstate->last_value,
+					   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormData_pg_sequence   *seq = sequence_read_options(seqh);
+	GaplessSequenceState   *seqstate;
+
+	seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (seqstate->xid != local_xid &&
+		   TransactionIdIsInProgress(seqstate->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seqstate->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = sequence_read_options(seqh);
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+				   int64 last_value, bool is_called)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..b49cec6
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index 4e3f337..9852933 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -116,6 +116,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
  &earthdistance;
  &file-fdw;
  &fuzzystrmatch;
+ &gapless-seq;
  &hstore;
  &intagg;
  &intarray;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index fd355dc..2681437 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -119,6 +119,7 @@
 <!ENTITY earthdistance   SYSTEM "earthdistance.sgml">
 <!ENTITY file-fdw        SYSTEM "file-fdw.sgml">
 <!ENTITY fuzzystrmatch   SYSTEM "fuzzystrmatch.sgml">
+<!ENTITY gapless-seq     SYSTEM "gapless-seq.sgml">
 <!ENTITY hstore          SYSTEM "hstore.sgml">
 <!ENTITY intagg          SYSTEM "intagg.sgml">
 <!ENTITY intarray        SYSTEM "intarray.sgml">
diff --git a/doc/src/sgml/gapless-seq.sgml b/doc/src/sgml/gapless-seq.sgml
new file mode 100644
index 0000000..44cc224
--- /dev/null
+++ b/doc/src/sgml/gapless-seq.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless-seq" xreflabel="gapless_seq">
+ <title>gapless_seq</title>
+
+ <indexterm zone="gapless-seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence and hold
+  lock on the sequence itself, so any concurrent nextval() calls have to wait
+  for the transaction to finish. This could be very costly and is not
+  recommended for general usage except in specific applications that require
+  this feature.
+ </para>
+
+</sect1>
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 45778fd..ac3f31d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15317,7 +15317,25 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
 	snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE);
 
-	if (fout->remoteVersion >= 80400)
+	if (fout->remoteVersion >= 90600)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT $$%s$$ AS sequence_name, "
+						  "start_value, increment_by, "
+				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
+				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
+						  "     ELSE max_value "
+						  "END AS max_value, "
+					"CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
+				   "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
+						  "     ELSE min_value "
+						  "END AS min_value, "
+						  "cache_value, is_cycled FROM %s",
+						  tbinfo->dobj.name, bufx, bufm,
+						  fmtId(tbinfo->dobj.name));
+
+	}
+	else if (fout->remoteVersion >= 80400)
 	{
 		appendPQExpBuffer(query,
 						  "SELECT sequence_name, "
@@ -15556,9 +15574,7 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 	 * use it for one with nondefault access method because we can produce
 	 * more backward compatible dump that way.
 	 */
-	if (fout->remoteVersion >= 90600 &&
-		tbinfo->relam != InvalidOid &&
-		tbinfo->relam != LOCAL_SEQAM_OID)
+	if (fout->remoteVersion >= 90600)
 	{
 		appendPQExpBuffer(query,
 						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
@@ -15583,9 +15599,7 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 
 	resetPQExpBuffer(query);
 
-	if (fout->remoteVersion >= 90600 &&
-		tbinfo->relam != InvalidOid &&
-		tbinfo->relam != LOCAL_SEQAM_OID)
+	if (fout->remoteVersion >= 90600)
 	{
 		char   *state = PQgetvalue(res, 0, 0);
 
#113Petr Jelinek
petr@2ndquadrant.com
In reply to: Fabrízio de Royes Mello (#112)
2 attachment(s)
Re: Sequence Access Method WIP

On 29/03/16 18:50, Fabr�zio de Royes Mello wrote:

On Tue, Mar 29, 2016 at 12:25 PM, David Steele <david@pgmasters.net
<mailto:david@pgmasters.net>> wrote:

Hi Petr,

On 3/28/16 3:11 PM, Fabr�zio de Royes Mello wrote:

fabrizio@bagual:~/pgsql
$ bin/pg_dump > /tmp/fabrizio.sql
pg_dump: [archiver (db)] query failed: ERROR: column "sequence_name"
does not exist
LINE 1: SELECT sequence_name, start_value, increment_by, CASE WHEN i...
^
pg_dump: [archiver (db)] query was: SELECT sequence_name, start_value,
increment_by, CASE WHEN increment_by > 0 AND max_value =
9223372036854775807 THEN NULL WHEN increment_by < 0 AND max_value =
-1 THEN NULL ELSE max_value END AS max_value, CASE WHEN
increment_by > 0 AND min_value = 1 THEN NULL WHEN increment_by < 0
AND min_value = -9223372036854775807 THEN NULL ELSE min_value END
AS min_value, cache_value, is_cycled FROM x

It looks like a new patch is needed. I've marked this "waiting on

author".

Yeah there were some incompatible commits since my last rebase, fixed,
along with the pg_dump bugs..

But there are other issue in the gapless-seq extension when I ran "make
check":

43 CREATE EXTENSION gapless_seq;
44 CREATE SEQUENCE test_gapless USING gapless;
45 SELECT nextval('test_gapless'::regclass);
46 ! ERROR: could not access status of transaction 1275068416
47 ! DETAIL: Could not open file "pg_subtrans/4C00": No such file or
directory.
48 BEGIN;
49 SELECT nextval('test_gapless'::regclass);
50 ! ERROR: could not access status of transaction 1275068416
51 ! DETAIL: Could not open file "pg_subtrans/4C00": No such file or
directory.
52 SELECT nextval('test_gapless'::regclass);
53 ! ERROR: current transaction is aborted, commands ignored until
end of transaction block
54 SELECT nextval('test_gapless'::regclass);
55 ! ERROR: current transaction is aborted, commands ignored until
end of transaction block
56 ROLLBACK;
57 SELECT nextval('test_gapless'::regclass);
58 ! ERROR: could not access status of transaction 1275068416
59 ! DETAIL: Could not open file "pg_subtrans/4C00": No such file or
directory.

And I see the same running manually:

fabrizio=# create extension gapless_seq;
CREATE EXTENSION
fabrizio=# create sequence x using gapless;
CREATE SEQUENCE
fabrizio=# select nextval('x');
ERROR: could not access status of transaction 1258291200
DETAIL: Could not open file "pg_subtrans/4B00": No such file or directory.

Regards,

Hmm I am unable to reproduce this. What OS? Any special configure flags
you use?

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0002-gapless-seq-2016-03-29.patchtext/x-diff; name=0002-gapless-seq-2016-03-29.patchDownload
From 38ad5cb3f7d5fe59d10380be75b49ee6b783c5ac Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Fri, 4 Mar 2016 15:03:44 +0100
Subject: [PATCH 2/2] gapless seq

---
 contrib/Makefile                             |   1 +
 contrib/gapless_seq/Makefile                 |  63 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  57 +++
 contrib/gapless_seq/gapless_seq.c            | 531 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 +++
 doc/src/sgml/contrib.sgml                    |   1 +
 doc/src/sgml/filelist.sgml                   |   1 +
 doc/src/sgml/gapless-seq.sgml                |  24 ++
 12 files changed, 950 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql
 create mode 100644 doc/src/sgml/gapless-seq.sgml

diff --git a/contrib/Makefile b/contrib/Makefile
index d12dd63..d2a0620 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
 		earthdistance	\
 		file_fdw	\
 		fuzzystrmatch	\
+		gapless_seq	\
 		hstore		\
 		intagg		\
 		intarray	\
diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..130104a
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+ERROR:  cannot drop access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..3b41f86
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,57 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_handler(internal)
+RETURNS SEQ_AM_HANDLER
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_in(cstring)
+RETURNS seqam_gapless_state
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_out(seqam_gapless_state)
+RETURNS cstring
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TYPE seqam_gapless_state (
+	INPUT   = seqam_gapless_state_in,
+	OUTPUT  = seqam_gapless_state_out,
+	INTERNALLENGTH = 16
+);
+COMMENT ON TYPE seqam_gapless_state IS 'state for gapless sequence am';
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	last_value bigint NOT NULL,
+	is_called bool NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relkind = 'S' AND relam = (
+			SELECT oid FROM pg_am WHERE amname = 'gapless' AND amtype = 'S'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD gapless TYPE SEQUENCE HANDLER seqam_gapless_handler;
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..57f1570
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,531 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager / Gapless sequence implementation
+ *
+ *------------------------------------------------------------
+ */
+
+typedef struct GaplessSequenceState
+{
+	int64   last_value;
+	uint32	xid;
+	bool	is_called;
+} GaplessSequenceState;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	int64	last_value;
+	bool	is_called;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static bytea *seqam_gapless_reloptions(Datum reloptions, bool validate);
+static Datum seqam_gapless_init(Relation seqrel, Form_pg_sequence seq,
+								int64 restart_value, bool restart_requested,
+								bool is_init);
+static int64 seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh,
+								 int64 nrequested, int64 *last);
+static void seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh,
+								 int64 new_value);
+static Datum seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+									Datum amstate);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_handler);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_in);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_out);
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+							   int64 last_value, bool is_called);
+
+Datum
+seqam_gapless_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr, *end;
+	Datum		d;
+	GaplessSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	end = ptr;
+	while (*end != ',' && *end != ')')
+	{
+		char		ch = *end++;
+
+		if (ch == '\0')
+			goto malformed;
+	}
+
+	d = DirectFunctionCall1(int8in,
+							CStringGetDatum(pnstrdup(ptr, end-ptr)));
+	state->last_value = DatumGetInt64(d);
+
+	/* is_called is optional */
+	if (*end == ',')
+	{
+		ptr = ++end;
+
+		end = state_str+strlen(state_str)-1;
+
+		if (*end != ')')
+			goto malformed;
+
+		d = DirectFunctionCall1(boolin,
+								CStringGetDatum(pnstrdup(ptr, end-ptr)));
+		state->is_called = DatumGetBool(d);
+	}
+	else
+		state->is_called = true;
+
+	state->xid = InvalidTransactionId;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_gapless_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_gapless_state_out(PG_FUNCTION_ARGS)
+{
+	GaplessSequenceState   *state;
+	StringInfoData			buf;
+
+	state = (GaplessSequenceState *) PG_GETARG_POINTER(0);
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s)",
+					 state->last_value, state->is_called ? "t" : "f");
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the gapless sequence access method.
+ */
+Datum
+seqam_gapless_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine   *seqam = makeNode(SeqAmRoutine);
+	Oid				nspid,
+					typid;
+
+	nspid = get_namespace_oid("gapless_seq", false);
+	typid = GetSysCacheOid2(TYPENAMENSP,
+							PointerGetDatum("seqam_gapless_state"),
+							ObjectIdGetDatum(nspid));
+
+	if (!OidIsValid(typid))
+		elog(ERROR, "cache lookup failed for type \"seqam_gapless_state\"");
+
+	seqam->StateTypeOid = typid;
+	seqam->amoptions = seqam_gapless_reloptions;
+	seqam->Init = seqam_gapless_init;
+	seqam->Alloc = seqam_gapless_alloc;
+	seqam->Setval = seqam_gapless_setval;
+	seqam->GetState = seqam_gapless_get_state;
+	seqam->SetState = seqam_gapless_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+static bytea *
+seqam_gapless_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+static Datum
+seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	Oid			seqrelid = seqrel->rd_id;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	GaplessSequenceState   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(GaplessSequenceState));
+	else
+		seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->xid = GetTopTransactionId();
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, valtuple, seqstate->last_value,
+						   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+static int64
+seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+					int64 *last)
+{
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	Form_pg_sequence		seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, result, true);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* We always WAL log for gapless sequence. */
+	sequence_start_update(seqh, true);
+	seqstate->last_value = result;
+	seqstate->is_called = true;
+	if (seqstate->xid != local_xid)
+		seqstate->xid = local_xid;
+	sequence_save_state(seqh, PointerGetDatum(seqstate), true);
+	sequence_finish_update(seqh);
+
+	*last = result;
+
+	return result;
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+static void
+seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+static Datum
+seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	GaplessSequenceState   *seqstate = palloc0(sizeof(GaplessSequenceState));
+
+	/*
+	 * Get the last value from the values table, if not found use the values
+	 * from sequence typle.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		seqstate->last_value = v->last_value;
+		seqstate->is_called = v->is_called;
+	}
+	else
+	{
+		Datum		amstate = sequence_read_state(seqh);
+
+		memcpy(seqstate, DatumGetPointer(amstate),
+			   sizeof(GaplessSequenceState));
+		sequence_release_tuple(seqh);
+	}
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+static void
+seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+						Datum amstate)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	FormData_pg_sequence   *seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(seqstate->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, seqstate->last_value,
+					   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormData_pg_sequence   *seq = sequence_read_options(seqh);
+	GaplessSequenceState   *seqstate;
+
+	seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (seqstate->xid != local_xid &&
+		   TransactionIdIsInProgress(seqstate->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seqstate->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = sequence_read_options(seqh);
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+				   int64 last_value, bool is_called)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..b49cec6
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index 4e3f337..9852933 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -116,6 +116,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
  &earthdistance;
  &file-fdw;
  &fuzzystrmatch;
+ &gapless-seq;
  &hstore;
  &intagg;
  &intarray;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index fd355dc..2681437 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -119,6 +119,7 @@
 <!ENTITY earthdistance   SYSTEM "earthdistance.sgml">
 <!ENTITY file-fdw        SYSTEM "file-fdw.sgml">
 <!ENTITY fuzzystrmatch   SYSTEM "fuzzystrmatch.sgml">
+<!ENTITY gapless-seq     SYSTEM "gapless-seq.sgml">
 <!ENTITY hstore          SYSTEM "hstore.sgml">
 <!ENTITY intagg          SYSTEM "intagg.sgml">
 <!ENTITY intarray        SYSTEM "intarray.sgml">
diff --git a/doc/src/sgml/gapless-seq.sgml b/doc/src/sgml/gapless-seq.sgml
new file mode 100644
index 0000000..44cc224
--- /dev/null
+++ b/doc/src/sgml/gapless-seq.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless-seq" xreflabel="gapless_seq">
+ <title>gapless_seq</title>
+
+ <indexterm zone="gapless-seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence and hold
+  lock on the sequence itself, so any concurrent nextval() calls have to wait
+  for the transaction to finish. This could be very costly and is not
+  recommended for general usage except in specific applications that require
+  this feature.
+ </para>
+
+</sect1>
-- 
1.9.1

0001-seqam-2016-03-29.patchtext/x-diff; name=0001-seqam-2016-03-29.patchDownload
From a3a098a938640b4e735f77093a087613cd9c8226 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Wed, 2 Mar 2016 16:15:33 +0100
Subject: [PATCH 1/2] seqam

---
 doc/src/sgml/filelist.sgml                    |    1 +
 doc/src/sgml/postgres.sgml                    |    1 +
 doc/src/sgml/ref/alter_sequence.sgml          |   55 +
 doc/src/sgml/ref/create_access_method.sgml    |    9 +-
 doc/src/sgml/ref/create_sequence.sgml         |   25 +
 doc/src/sgml/seqam.sgml                       |  312 +++++
 src/backend/access/Makefile                   |    4 +-
 src/backend/access/common/reloptions.c        |   13 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqamapi.c        |  191 ++++
 src/backend/access/sequence/seqlocal.c        |  408 +++++++
 src/backend/bootstrap/bootparse.y             |    1 +
 src/backend/catalog/heap.c                    |   17 +-
 src/backend/catalog/objectaddress.c           |    1 +
 src/backend/catalog/toasting.c                |    1 +
 src/backend/commands/amcmds.c                 |   38 +-
 src/backend/commands/cluster.c                |    1 +
 src/backend/commands/createas.c               |    3 +-
 src/backend/commands/indexcmds.c              |    2 +-
 src/backend/commands/sequence.c               | 1502 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |   49 +-
 src/backend/commands/typecmds.c               |    3 +-
 src/backend/commands/view.c                   |    3 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   91 +-
 src/backend/parser/parse_utilcmd.c            |    3 +
 src/backend/tcop/utility.c                    |    6 +-
 src/backend/utils/adt/pseudotypes.c           |   25 +
 src/backend/utils/cache/relcache.c            |   53 +-
 src/backend/utils/misc/guc.c                  |    1 +
 src/bin/pg_dump/pg_dump.c                     |  132 ++-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/psql/describe.c                       |  112 +-
 src/include/access/reloptions.h               |    2 +-
 src/include/access/seqamapi.h                 |  109 ++
 src/include/catalog/heap.h                    |    1 +
 src/include/catalog/pg_am.h                   |    5 +
 src/include/catalog/pg_proc.h                 |   19 +
 src/include/catalog/pg_type.h                 |    7 +
 src/include/commands/defrem.h                 |    1 +
 src/include/commands/sequence.h               |   31 +-
 src/include/commands/tablecmds.h              |    2 +-
 src/include/nodes/nodes.h                     |    1 +
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/utils/builtins.h                  |    2 +
 src/include/utils/rel.h                       |    8 +-
 src/test/regress/expected/create_am.out       |   19 +
 src/test/regress/expected/create_view.out     |    5 +-
 src/test/regress/expected/opr_sanity.out      |    4 +-
 src/test/regress/expected/sequence.out        |   35 +-
 src/test/regress/expected/updatable_views.out |   95 +-
 src/test/regress/sql/create_am.sql            |   16 +
 src/test/regress/sql/create_view.sql          |    5 +-
 src/test/regress/sql/opr_sanity.sql           |    4 +-
 src/test/regress/sql/sequence.sql             |   10 +-
 56 files changed, 2694 insertions(+), 784 deletions(-)
 create mode 100644 doc/src/sgml/seqam.sgml
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqamapi.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqamapi.h

diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 30adece..fd355dc 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 7e82cdc..d37058a 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -251,6 +251,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
index 3c091f8..8676a84 100644
--- a/doc/src/sgml/ref/create_access_method.sgml
+++ b/doc/src/sgml/ref/create_access_method.sgml
@@ -61,7 +61,8 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
     <listitem>
      <para>
       This clause specifies type of access method to define.
-      Only <literal>INDEX</literal> is supported at present.
+      Supported values are <literal>INDEX</literal> for index access methods
+      and <literal>SEQUENCE</literal> for sequence access methods.
      </para>
     </listitem>
    </varlistentry>
@@ -75,7 +76,9 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
       of access method to the core.  The handler function must take single
       argument of type <type>internal</>, and its return type depends on the
       type of access method; for <literal>INDEX</literal> access methods, it
-      must be <type>index_am_handler</type>.
+      must be <type>index_am_handler</type> and for
+      <literal>SEQUENCE</literal> access methods it must be
+      <type>sequence_am_handler</type>.
      </para>
 
      <para>
@@ -114,6 +117,8 @@ CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;
    <member><xref linkend="sql-drop-access-method"></member>
    <member><xref linkend="sql-createopclass"></member>
    <member><xref linkend="sql-createopfamily"></member>
+   <member><xref linkend="index-api"> </member>
+   <member><xref linkend="seqam"></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..5faeae3
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,312 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_am</structname> system catalog (see
+   <xref linkend="catalog-pg-am">).
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The SQL interface of the sequence access method consists of a handler
+   function which has to return special pseudo-type
+   <type>sequence_handler</type>. This function must be written in a compiled
+   language such as C, using the version-1 interface. For details on C
+   language calling conventions and dynamic loading, see
+   <xref linkend="xfunc-c">.
+  </para>
+
+  <para>
+   The actual C structure returned by the handler function is following:
+<programlisting>
+typedef struct SeqAmRoutine
+{
+    NodeTag        type;
+
+    /* Custom datatype used for storing state of the sequence by the AM */
+    Oid            StateTypeOid;
+
+    /* Function for parsing reloptions */
+    ParseRelOptions_function    amoptions;
+
+    /* Initialization */
+    SeqAMInit_function          Init;
+
+    /* nextval handler */
+    SeqAMAlloc_function         Alloc;
+
+    /* State manipulation functions */
+    SeqAMSetval_function        Setval;
+    SeqAMGetState_function      GetState;
+    SeqAMSetState_function      SetState;
+} SeqAmRoutine;
+</programlisting>
+  </para>
+
+  <para>
+   These are the functions implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+Datum
+SeqAMInit (Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+           bool restart_requested, bool is_init)
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   The <literal>seq</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the Datum of a state of the sequence. The type
+   of the Datum is the <structname>StateTypeOid</structname> type.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+ParseRelOptions (Datum reloptions, bool validate)
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+SeqAMAlloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+            int64 *last)
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum SeqAMGetState (Relation seqrel, SequenceHandle *seqh)
+</programlisting>
+   Dump the current state of the sequence. The return value is the Datum of
+   a <structname>StateTypeOid</structname>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetState (Relation seqrel, SequenceHandle *seqh, Datum amstate)
+</programlisting>
+   Restore state of the sequence based on amstate which is Datum of a
+   <structname>StateTypeOid</structname> type.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following helper functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh)
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+Form_pg_sequence
+(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns the whole pg_sequence tuple as the
+   <structname>Form_pg_sequence</structname> structure.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum
+sequence_read_state(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns just the sequence access method specific state Datum.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_save_state(SequenceHandle *seqh, Datum seqstate, bool dowal)
+</programlisting>
+   Save the modified sequence state tuple indicating if the change should be
+   WAL logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh)
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_save_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh)
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_options</>
+   and/or added by <function>sequence_read_state</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_save_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors)
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname)
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a..223403d 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,7 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  tablesample transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc sequence \
+			  spgist tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ea0755a..bb368d2 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -888,8 +888,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
- * AM's options parser function in the case of a tuple corresponding to an
- * index, or NULL otherwise.
+ * or sequence AM's options parser function in the case of a tuple corresponding
+ * to an index or sequence respectively, or NULL otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
@@ -921,7 +921,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1374,14 +1375,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for access methods.
  *
- *	amoptions	index AM's option parser function
+ *	amoptions	AM's option parser function
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+am_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 {
 	Assert(amoptions != NULL);
 
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..1784799
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqamapi.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqamapi.c b/src/backend/access/sequence/seqamapi.c
new file mode 100644
index 0000000..bfdd589
--- /dev/null
+++ b/src/backend/access/sequence/seqamapi.c
@@ -0,0 +1,191 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqamapi.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amstate.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_am.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * GetSeqAmRoutine - call the specified access method handler routine
+ * to get its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutine(Oid amhandler)
+{
+	Datum			datum;
+	SeqAmRoutine   *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SeqAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SeqAmRoutine))
+		elog(ERROR, "sequence access method handler function %u did not return an SeqAmRoutine struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * GetSeqAmRoutineByAMId - look up the handler of the sequence access method
+ * for the given OID, and retrieve its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineByAMId(Oid amoid)
+{
+	HeapTuple		tuple;
+	regproc			amhandler;
+	Form_pg_am		amform;
+
+	/* 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);
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("sequence access method \"%s\" does not have a handler",
+						NameStr(amform->amname))));
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function. */
+	return GetSeqAmRoutine(amhandler);
+}
+
+/*
+ * GetSeqAmRoutineForRelation - look up the handler of the sequence access
+ * method for the given sequence Relation.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineForRelation(Relation relation)
+{
+	SeqAmRoutine *seqam;
+	SeqAmRoutine *cseqam;
+
+	if (relation->rd_seqamroutine == NULL)
+	{
+		if (!OidIsValid(relation->rd_rel->relam))
+		{
+			char *relname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(relation)),
+													   RelationGetRelationName(relation));
+			elog(ERROR, "relation %s isn't sequence", relname);
+		}
+
+		seqam = GetSeqAmRoutineByAMId(relation->rd_rel->relam);
+		/* Save the data for later reuse in CacheMemoryContext */
+		cseqam = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												   sizeof(SeqAmRoutine));
+		memcpy(cseqam, seqam, sizeof(SeqAmRoutine));
+		relation->rd_seqamroutine = cseqam;
+	}
+
+	return relation->rd_seqamroutine;
+}
+
+/*
+ * ValidateSeqAmRoutine - to sanity check for the SeqAmRoutine returned by a
+ * handler.
+ *
+ * At the moment we only check that the StateTypeOid is valid type oid and that
+ * the type it points to is fixed width and will fit into the sequence page.
+ */
+void
+ValidateSeqAmRoutine(SeqAmRoutine *routine)
+{
+#define MAX_SEQAM_STATE_LEN \
+	MaxHeapTupleSize - MinHeapTupleSize - MAXALIGN(offsetof(FormData_pg_sequence, amstate))
+
+	Oid	stattypeoid = routine->StateTypeOid;
+
+	if (!get_typisdefined(stattypeoid))
+		ereport(ERROR,
+				(errmsg("type with OID %u does not exist",
+						stattypeoid)));
+
+	if (TypeIsToastable(stattypeoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" must not be toastable",
+						format_type_be(stattypeoid))));
+
+	if (get_typlen(stattypeoid) >= MAX_SEQAM_STATE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" exceeds maximum allowed length (%lu)",
+						format_type_be(stattypeoid), MAX_SEQAM_STATE_LEN)));
+
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..2d31478
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,408 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+#include "utils/typcache.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+static bytea *seqam_local_reloptions(Datum reloptions, bool validate);
+static Datum seqam_local_init(Relation seqrel, Form_pg_sequence seq,
+							  int64 restart_value, bool restart_requested,
+							  bool is_init);
+static int64 seqam_local_alloc(Relation seqrel, SequenceHandle *seqh,
+							   int64 nrequested, int64 *last);
+static void seqam_local_setval(Relation seqrel, SequenceHandle *seqh,
+							   int64 new_value);
+static Datum seqam_local_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+								  Datum amstate);
+
+static char *
+seqam_local_state_get_part(StringInfo buf, char *ptr, bool *found)
+{
+	*found = false;
+
+	resetStringInfo(buf);
+
+	while (*ptr != ',' && *ptr != ')')
+	{
+		char		ch = *ptr++;
+
+		if (ch == '\0')
+		{
+			*found = false;
+			return ptr;
+		}
+
+		appendStringInfoChar(buf, ch);
+
+		if (!*found)
+			*found = true;
+	}
+
+	return ptr;
+}
+
+Datum
+seqam_local_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr;
+	Datum		d;
+	bool		found;
+	StringInfoData		buf;
+	LocalSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	initStringInfo(&buf);
+
+	/* last_value is required */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (!found)
+		goto malformed;
+	d = DirectFunctionCall1(int8in, CStringGetDatum(buf.data));
+	state->last_value = DatumGetInt64(d);
+	if (*ptr == ',')
+		ptr++;
+
+	/* is_called is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(boolin, CStringGetDatum(buf.data));
+		state->is_called = DatumGetBool(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->is_called = true;
+
+	/* log_cnt is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(int4in, CStringGetDatum(buf.data));
+		state->log_cnt = DatumGetInt32(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->log_cnt = 0;
+
+	if (*ptr != ')' || *(++ptr) != '\0')
+		goto malformed;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_local_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_local_state_out(PG_FUNCTION_ARGS)
+{
+	LocalSequenceState *state = (LocalSequenceState *) PG_GETARG_POINTER(0);
+	StringInfoData		buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s,%d)",
+					 state->last_value, state->is_called ? "t" : "f",
+					 state->log_cnt);
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the local sequence access method.
+ */
+Datum
+seqam_local_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine *seqam = makeNode(SeqAmRoutine);
+
+	seqam->StateTypeOid = SEQAMLOCALSTATEOID;
+	seqam->amoptions = seqam_local_reloptions;
+	seqam->Init = seqam_local_init;
+	seqam->Alloc = seqam_local_alloc;
+	seqam->Setval = seqam_local_setval;
+	seqam->GetState = seqam_local_get_state;
+	seqam->SetState = seqam_local_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+static bytea *
+seqam_local_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+static Datum
+seqam_local_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	LocalSequenceState	   *localseq;
+
+	if (is_init)
+		localseq = palloc0(sizeof(LocalSequenceState));
+	else
+		localseq = (LocalSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		localseq->last_value = restart_value;
+		localseq->is_called = false;
+	}
+
+	localseq->log_cnt = 0;
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+static int64
+seqam_local_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+				  int64 *last)
+{
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) seq->amstate;
+
+	next = result = localseq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = localseq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_start_update(seqh, logit);
+
+	if (logit)
+	{
+		localseq->last_value = next;
+		localseq->log_cnt = 0;
+		localseq->is_called = true;
+
+		sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	}
+
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_save_state(seqh, PointerGetDatum(localseq), false);
+
+	sequence_finish_update(seqh);
+
+	return result;
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+static void
+seqam_local_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq;
+
+	amstate = sequence_read_state(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	localseq->last_value = new_value;		/* last fetched number */
+	localseq->is_called = true;
+	localseq->log_cnt = 0;			/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+static Datum
+seqam_local_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq = palloc(sizeof(LocalSequenceState));
+
+	amstate = sequence_read_state(seqh);
+	memcpy(localseq, DatumGetPointer(amstate), sizeof(LocalSequenceState));
+	sequence_release_tuple(seqh);
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+static void
+seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+					  Datum amstate)
+{
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(localseq->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 41d2fd4..e684f60 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -251,6 +251,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e997b57..b7cd40a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -41,6 +41,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
@@ -88,6 +89,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -849,6 +851,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -922,6 +925,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1035,6 +1039,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1263,6 +1268,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1275,7 +1281,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the pg_am record.
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1330,6 +1337,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = AccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index cb3ba85..b40f0c7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index f40a005..40b5e91 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -291,6 +291,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 68ea5f3..1f2e0c4 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -15,6 +15,8 @@
 
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_am.h"
@@ -29,7 +31,7 @@
 #include "utils/syscache.h"
 
 
-static Oid	lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid	lookup_am_handler_func(List *handler_name, char amtype);
 static const char *get_am_type_string(char amtype);
 
 
@@ -72,7 +74,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	/*
 	 * Get the handler function oid, verifying the AM type while at it.
 	 */
-	amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+	amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
 
 	/*
 	 * Insert tuple into pg_am.
@@ -187,6 +189,16 @@ get_index_am_oid(const char *amname, bool missing_ok)
 }
 
 /*
+ * get_seq_am_oid - given an access method name, look up its OID
+ *		and verify it corresponds to an sequence AM.
+ */
+Oid
+get_seq_am_oid(const char *amname, bool missing_ok)
+{
+	return get_am_type_oid(amname, AMTYPE_SEQUENCE, missing_ok);
+}
+
+/*
  * get_am_oid - given an access method name, look up its OID.
  * 		The type is not checked.
  */
@@ -226,6 +238,8 @@ get_am_type_string(char amtype)
 	{
 		case AMTYPE_INDEX:
 			return "INDEX";
+		case AMTYPE_SEQUENCE:
+			return "SEQUENCE";
 		default:
 			/* shouldn't happen */
 			elog(ERROR, "invalid access method type '%c'", amtype);
@@ -240,9 +254,10 @@ get_am_type_string(char amtype)
  * This function either return valid function Oid or throw an error.
  */
 static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
 {
 	Oid			handlerOid;
+	Oid			handler_type;
 	static const Oid funcargtypes[1] = {INTERNALOID};
 
 	if (handler_name == NIL)
@@ -257,16 +272,21 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
 	switch (amtype)
 	{
 		case AMTYPE_INDEX:
-			if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
-				ereport(ERROR,
-						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-						 errmsg("function %s must return type \"%s\"",
-								NameListToString(handler_name),
-								"index_am_handler")));
+			handler_type = INDEX_AM_HANDLEROID;
+			break;
+		case AMTYPE_SEQUENCE:
+			handler_type = SEQ_AM_HANDLEROID;
 			break;
 		default:
 			elog(ERROR, "unrecognized access method type \"%c\"", amtype);
 	}
 
+	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;
 }
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5cb28cf..54a22da 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -680,6 +680,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cb7a145..ab7b7a3 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 13b04e6..92ae4db 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -536,7 +536,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..ad4159b 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,16 +14,22 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/tupmacs.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +42,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,24 +82,112 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	Oid			statetyp;
+	int16		statetyplen;
+	bool		statetypbyval;
+	HeapTupleData tup;
+	bool		inupdate;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
  */
 static SeqTableData *last_used_seq = NULL;
 
+static HeapTuple build_seq_tuple(Relation rel, SeqAmRoutine *seqam,
+								 Form_pg_sequence new, int64 restart_value);
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+
+/*
+ * Build template column definition for a sequence relation.
+*/
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_not_null = true;
+	coldef->is_from_type = false;
+	/* Force plain storage. */
+	coldef->storage = 'p';
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(Oid amstateTypeOid)
+{
+	List	   *seqcols;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		switch (colid)
+		{
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+			case SEQ_COL_AMSTATE:
+				coldef->typeName = makeTypeNameFromOid(amstateTypeOid, -1);
+				coldef->colname = "amstate";
+				break;
+		}
+
+		seqcols = lappend(seqcols, coldef);
+	}
 
+	return seqcols;
+}
 
 /*
  * DefineSequence
@@ -111,14 +200,12 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
+	List	   *seqcols;
+	SeqAmRoutine *seqam;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -147,102 +234,33 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
-	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
-
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
-		{
-			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
-				break;
-			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
-				break;
-			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
-				break;
-			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
-				break;
-			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
-				break;
-			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
-			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
-				break;
-		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
-	}
+	if (seq->accessMethod)
+		seqamid = get_seq_am_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
+	seqam = GetSeqAmRoutineByAMId(seqamid);
+	/* Build column definitions. */
+	seqcols = BuildSeqColumnDefList(seqam->StateTypeOid);
 
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
+	stmt->tableElts = seqcols;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	/* Build new sequence tuple and store it. */
+	tuple = build_seq_tuple(rel, seqam, &new, new.start_value);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +272,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +286,89 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
+	TupleDesc			tupDesc;
+	Datum	   values[SEQ_COL_LASTCOL];
+	bool	   nulls[SEQ_COL_LASTCOL];
+	SeqAmRoutine *seqam;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
-	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tupDesc = RelationGetDescr(seqrel);
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(seqrel, seq, seq->start_value,
+											  true, false);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
+}
+
+/*
+ * Build sequence tuple based on the sequence form and fill in the
+ * sequence AM specific info as well.
+ */
+static HeapTuple
+build_seq_tuple(Relation rel, SeqAmRoutine *seqam, Form_pg_sequence new,
+				int64 restart_value)
+{
+	TupleDesc	tupDesc;
+	HeapTuple	tuple;
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+
+	tupDesc = RelationGetDescr(rel);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new->start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new->increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new->max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new->min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new->cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new->is_cycled);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(rel, new, restart_value,
+											  false, true);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	return tuple;
 }
 
 /*
@@ -361,7 +411,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +431,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,6 +439,69 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 }
 
 /*
+ * Replace the type of amstate column.
+ *
+ * We don't do AlterTable here as that produces dead columns which we don't
+ * want. This is safe because the sequence page is controlled by code in this
+ * module and isn't changed the same way as a table.
+ *
+ * TODO: check if anybody is depending on the row-type associated with the
+ * sequence.
+ */
+static void
+replace_sequence_amstate_col(Oid seqrelid, Oid typid)
+{
+	Relation	attr_rel;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	HeapTuple	tp,
+				attr_tuple,
+				newattr_tuple;
+	Form_pg_type	typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+
+	memset(nulls, 0, sizeof(nulls));
+	memset(replace, 0, sizeof(replace));
+
+	replace[Anum_pg_attribute_atttypid - 1] = true;
+	replace[Anum_pg_attribute_attlen - 1] = true;
+	replace[Anum_pg_attribute_attbyval - 1] = true;
+	replace[Anum_pg_attribute_attalign - 1] = true;
+
+	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(typid);
+	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(typtup->typlen);
+	values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(typtup->typbyval);
+	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(typtup->typalign);
+
+	/* Build DROP command for amstate of old AM. */
+	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+	attr_tuple = SearchSysCache2(ATTNUM,
+								 ObjectIdGetDatum(seqrelid),
+								 Int16GetDatum(SEQ_COL_AMSTATE));
+	if (!HeapTupleIsValid(attr_tuple))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 SEQ_COL_AMSTATE, seqrelid);
+
+	newattr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel),
+									  values, nulls, replace);
+	simple_heap_update(attr_rel, &newattr_tuple->t_self, newattr_tuple);
+	CatalogUpdateIndexes(attr_rel, newattr_tuple);
+
+	ReleaseSysCache(tp);
+	heap_freetuple(newattr_tuple);
+	ReleaseSysCache(attr_tuple);
+
+	heap_close(attr_rel, RowExclusiveLock);
+}
+
+/*
  * AlterSequence
  *
  * Modify the definition of a sequence relation
@@ -406,19 +509,24 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence seq,
+					 new;
 	List	   *owned_by;
 	ObjectAddress address;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
+	SeqAmRoutine *oldseqam;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +534,187 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
+	oldamid = seqrel->rd_rel->relam;
+	oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	if (stmt->accessMethod)
+		seqamid = get_seq_am_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(stmt->options, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		HeapTuple       seqamtup;
+		Form_pg_am		form_am;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
+		SeqAmRoutine	   *newseqam;
+
+		oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = oldseqam->Alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
+
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(AMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
+
+		newseqam = GetSeqAmRoutineByAMId(seqamid);
+
+		form_am = (Form_pg_am) GETSTRUCT(seqamtup);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		validnsps[0] = NameStr(form_am->amname);
+		validnsps[1] = NULL;
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		(void) am_reloptions(newseqam->amoptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-		PageSetLSN(page, recptr);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
+
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
+
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * And change the type definition.
+		 *
+		 * We can't use AlterTable internals here because the sequence
+		 * has to have the expected number of columns and no
+		 * attisdropped = true columns.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		replace_sequence_amstate_col(seqrelid, newseqam->StateTypeOid);
+		CommandCounterIncrement();
+
+		/* Rebuild the sequence tuple and save it. */
+		tuple = build_seq_tuple(seqrel, newseqam, new, restart_value);
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										AccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = AccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
+	else
+	{
+		Datum	   newamstate;
 
-	END_CRIT_SECTION();
+		sequence_check_range(restart_value, new->min_value, new->max_value,
+							 restart_requested ? "RESTART" : "START");
 
-	UnlockReleaseBuffer(buf);
+		/* Let the new sequence AM initialize. */
+		newamstate = oldseqam->Init(seqrel, new, restart_value,
+									restart_requested, false);
+
+		sequence_start_update(&seqh, true);
+		memcpy(seq, new, offsetof(FormData_pg_sequence, amstate));
+		sequence_save_state(&seqh, newamstate, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +755,26 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +799,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
-
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam->Alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +816,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for syncrep.
-	 * It's sufficient to ensure the toplevel transaction has an xid, no need
-	 * to assign xids subxacts, that'll already trigger an appropriate wait.
-	 * (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +827,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +888,26 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,106 +924,86 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
-
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
+	seqam->Setval(seqrel, &seqh, next);
 
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	last_used_seq = elm;
 
-	UnlockReleaseBuffer(buf);
+	sequence_close(&seqh);
 
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	LocalSequenceState	state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
+
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
 
-	do_setval(relid, next, true);
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Build the state and pass it to sequence AM. */
+	state.last_value = next;
+	state.log_cnt = 0;
+	state.is_called = iscalled;
+	seqam->SetState(seqh.rel, &seqh, PointerGetDatum(&state));
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-	PG_RETURN_INT64(next);
-}
+	/* Reset local cached data */
+	elm->cached = elm->last;
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1034,11 +1062,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1117,58 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->statetyp = 6025; /* TODO */
+	seqh->statetyplen = -1;
+	seqh->statetypbyval = false;
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(!seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1178,170 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+Form_pg_sequence
+sequence_read_options(SequenceHandle *seqh)
+{
+	return (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
+}
+
+Datum
+sequence_read_state(SequenceHandle *seqh)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+
+	return PointerGetDatum(seq->amstate);
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool dowal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+
+	if (seqh->statetyplen < 0)
+	{
+		get_typlenbyval(seqh->statetyp, &seqh->statetyplen,
+						&seqh->statetypbyval);
+		Assert(seqh->statetyplen > 0);
+	}
+
+	if (dowal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_save_state(SequenceHandle *seqh, Datum amstate, bool dowal)
+{
+	HeapTuple			tup = sequence_read_tuple(seqh);
+	Form_pg_sequence	seq = (Form_pg_sequence) GETSTRUCT(tup);
+	Page				page;
+
+	/*
+	 * Update the state data inline.
+	 *
+	 * This is only needed when the provided amstate datum points to different
+	 * data than what is already in the tuple.
+	 */
+	if (DatumGetPointer(amstate) != seq->amstate)
+	{
+		if (seqh->statetypbyval)
+			store_att_byval(seq->amstate, amstate, seqh->statetyplen);
+		else
+			memmove(seq->amstate, DatumGetPointer(amstate), seqh->statetyplen);
+	}
+
+	page = BufferGetPage(seqh->buf);
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (dowal && RelationNeedsWAL(seqh->rel))
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	Assert(BufferIsValid(seqh->buf));
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1351,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1428,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1436,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1445,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1453,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1460,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1473,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1526,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1540,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1650,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1678,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1686,85 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Datum		state;
+	char	   *statestr;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typoutput;
+	bool		typisvarlena;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type output function. */
+	getTypeOutputInfo(seqam->StateTypeOid, &typoutput, &typisvarlena);
+
+	/* Get the output and convert it to string. */
+	state = seqam->GetState(seqh.rel, &seqh);
+	statestr = OidOutputFunctionCall(typoutput, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_TEXT_P(cstring_to_text(statestr));
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *statestr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typinput,
+				typioparam;
+	Datum		state;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type input function. */
+	getTypeInputInfo(seqam->StateTypeOid, &typinput, &typioparam);
+
+	/* Convert the string to the state type and set it as new state. */
+	state = OidInputFunctionCall(typinput, statestr, typioparam, -1);
+	seqam->SetState(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1830,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 96dc923..6e6ee5d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -269,6 +270,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -454,7 +456,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -473,7 +475,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -551,13 +552,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		SeqAmRoutine *seqam;
+
+		Assert(relamid != InvalidOid);
+		seqam = GetSeqAmRoutineByAMId(relamid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 NULL, true, false);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		(void) am_reloptions(seqam->amoptions, reloptions, true);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -678,6 +695,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3308,7 +3326,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW |
+								ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4308,6 +4327,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4351,8 +4373,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a table, view, or foreign table");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9403,12 +9425,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqamroutine->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
+					 errmsg("\"%s\" is not a table, view, materialized view, index, sequence, or TOAST table",
 							RelationGetRelationName(rel))));
 			break;
 	}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 3de29b7..7aab5ab 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2116,7 +2116,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e9d9ba2..ca0f481 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6378db8..8c19b31 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3564,7 +3564,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3577,7 +3579,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 854c062..5972ca6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1627,7 +1627,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1638,7 +1640,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1273352..75af985 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -310,7 +310,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <list>	event_trigger_when_list event_trigger_value_list
 %type <defelt>	event_trigger_when_item
-%type <chr>		enable_trigger
+%type <chr>		enable_trigger am_type
 
 %type <str>		copy_file_name
 				database_name access_method_clause access_method attr_name
@@ -605,7 +605,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 METHOD 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
@@ -3587,7 +3588,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3609,7 +3636,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3617,11 +3668,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3680,7 +3754,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
@@ -4715,16 +4789,21 @@ row_security_cmd:
  *
  *****************************************************************************/
 
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
 				{
 					CreateAmStmt *n = makeNode(CreateAmStmt);
 					n->amname = $4;
 					n->handler_name = $8;
-					n->amtype = AMTYPE_INDEX;
+					n->amtype = $6;
 					$$ = (Node *) n;
 				}
 		;
 
+am_type:
+			INDEX		{ $$ = AMTYPE_INDEX; }
+		|	SEQUENCE	{ $$ = AMTYPE_SEQUENCE; }
+		;
+
 /*****************************************************************************
  *
  *		QUERIES :
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6528494..509fa32 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -29,6 +29,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -456,6 +457,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4d0aac9..8b95577 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -965,7 +965,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -998,7 +999,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index dd447cf..6c1456e 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -399,6 +399,31 @@ index_am_handler_out(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();			/* keep compiler quiet */
 }
 
+/*
+ * seq_am_handler_int		- input routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_in(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * seq_am_handler_out		- output routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_out(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot display a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
 
 /*
  * tsm_handler_in		- input routine for pseudo-type TSM_HANDLER.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 130c06d..b9686da 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -260,6 +261,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -424,6 +426,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	amoptions_function	amoptions = NULL;
 
 	relation->rd_options = NULL;
 
@@ -432,10 +435,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_amroutine->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqamroutine->amoptions;
+			break;
 		default:
 			return;
 	}
@@ -447,8 +455,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_amroutine->amoptions : NULL);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1049,11 +1056,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1555,6 +1565,34 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation relation)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	SeqAmRoutine	   *result, *tmp;
+
+	/*
+	 * Look up the index's access method, save the OID of its handler function
+	 */
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 relation->rd_rel->relam);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amtype == AMTYPE_SEQUENCE);
+	relation->rd_amhandler = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	result = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												 sizeof(SeqAmRoutine));
+	tmp = GetSeqAmRoutine(relation->rd_amhandler);
+	memcpy(result, tmp, sizeof(SeqAmRoutine));
+	relation->rd_seqamroutine = result;
+}
 
 /*
  *		formrdesc
@@ -4885,6 +4923,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqamroutine == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 65a6cd4..074406a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqamapi.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 077b37e..2b8c058 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4747,6 +4747,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4798,6 +4799,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4840,6 +4842,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4882,6 +4885,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4924,6 +4928,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4964,6 +4969,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5003,6 +5009,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5042,6 +5049,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5081,6 +5089,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5119,6 +5128,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5153,6 +5163,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5182,6 +5193,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5221,6 +5233,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5274,6 +5287,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5339,6 +5353,10 @@ getTables(Archive *fout, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -11577,6 +11595,9 @@ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
 		case AMTYPE_INDEX:
 			appendPQExpBuffer(q, "TYPE INDEX ");
 			break;
+		case AMTYPE_SEQUENCE:
+			appendPQExpBuffer(q, "TYPE SEQUENCE ");
+			break;
 		default:
 			write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
 					  aminfo->amtype, qamname);
@@ -15281,7 +15302,8 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -15298,8 +15320,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	if (fout->remoteVersion >= 80400)
 	{
 		appendPQExpBuffer(query,
-						  "SELECT sequence_name, "
-						  "start_value, increment_by, "
+						  "SELECT start_value, increment_by, "
 				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
 				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
 						  "     ELSE max_value "
@@ -15315,8 +15336,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	else
 	{
 		appendPQExpBuffer(query,
-						  "SELECT sequence_name, "
-						  "0 AS start_value, increment_by, "
+						  "SELECT 0 AS start_value, increment_by, "
 				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
 				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
 						  "     ELSE max_value "
@@ -15341,24 +15361,40 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 		exit_nicely(1);
 	}
 
-	/* Disable this check: it fails if sequence has been renamed */
-#ifdef NOT_USED
-	if (strcmp(PQgetvalue(res, 0, 0), tbinfo->dobj.name) != 0)
+	startv = PQgetvalue(res, 0, 0);
+	incby = PQgetvalue(res, 0, 1);
+	if (!PQgetisnull(res, 0, 2))
+		maxv = PQgetvalue(res, 0, 2);
+	if (!PQgetisnull(res, 0, 3))
+		minv = PQgetvalue(res, 0, 3);
+	cache = PQgetvalue(res, 0, 4);
+	cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0);
+
+	/* 9.6 adds sequence access methods */
+	if (fout->remoteVersion >= 90600)
 	{
-		write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
-				  tbinfo->dobj.name, PQgetvalue(res, 0, 0));
-		exit_nicely(1);
-	}
-#endif
+		PGresult   *res2;
 
-	startv = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
-	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
-	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
-	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
+		printfPQExpBuffer(query, "SELECT a.amname\n"
+								 "FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
 
 	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
@@ -15401,6 +15437,13 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -15467,6 +15510,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15483,16 +15529,23 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/* On 9.6 there is special interface for dumping sequences */
+	if (fout->remoteVersion >= 90600)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15505,14 +15558,27 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90600)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index c02c536..c62abcc 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -231,6 +231,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_class) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 7b2f4e6..ac85da3 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1403,30 +1403,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1470,13 +1446,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1805,6 +1823,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1814,12 +1834,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.amname\n"
+								"FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 469ac67..69135aa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -273,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
+extern bytea *am_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqamapi.h b/src/include/access/seqamapi.h
new file mode 100644
index 0000000..a4c826a
--- /dev/null
+++ b/src/include/access/seqamapi.h
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqamapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAMAPI_H
+#define SEQAMAPI_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef bytea * (*ParseRelOptions_function) (Datum reloptions,
+											 bool validate);
+
+typedef Datum (*SeqAMInit_function) (Relation seqrel,
+									 Form_pg_sequence seq,
+									 int64 restart_value,
+									 bool restart_requested,
+									 bool is_init);
+
+typedef int64 (*SeqAMAlloc_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 nrequested,
+									  int64 *last);
+
+typedef void (*SeqAMSetval_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 new_value);
+
+typedef Datum (*SeqAMGetState_function) (Relation seqrel,
+										 SequenceHandle *seqh);
+typedef void (*SeqAMSetState_function) (Relation seqrel, SequenceHandle *seqh,
+										Datum amstate);
+
+typedef struct SeqAmRoutine
+{
+	NodeTag		type;
+
+	/* Custom columns needed by the AM */
+	Oid			StateTypeOid;
+
+	/* Function for parsing reloptions */
+	ParseRelOptions_function	amoptions;
+
+	/* Initialization */
+	SeqAMInit_function			Init;
+
+	/* nextval handler */
+	SeqAMAlloc_function			Alloc;
+
+	/* State manipulation functions */
+	SeqAMSetval_function		Setval;
+	SeqAMGetState_function		GetState;
+	SeqAMSetState_function		SetState;
+} SeqAmRoutine;
+
+extern SeqAmRoutine *GetSeqAmRoutine(Oid seqamhandler);
+extern SeqAmRoutine *GetSeqAmRoutineByAMId(Oid amoid);
+extern SeqAmRoutine *GetSeqAmRoutineForRelation(Relation relation);
+extern void ValidateSeqAmRoutine(SeqAmRoutine *routine);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern Form_pg_sequence sequence_read_options(SequenceHandle *seqh);
+extern Datum sequence_read_state(SequenceHandle *seqh);
+extern void sequence_start_update(SequenceHandle *seqh, bool dowal);
+extern void sequence_save_state(SequenceHandle *seqh, Datum seqstate,
+								bool dowal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+/* We need this public for serval3 */
+typedef struct LocalSequenceState
+{
+	int64   last_value;
+	int32	log_cnt;
+	bool	is_called;
+} LocalSequenceState;
+
+extern Datum seqam_local_state_in(PG_FUNCTION_ARGS);
+extern Datum seqam_local_state_out(PG_FUNCTION_ARGS);
+extern Datum seqam_local_handler(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAMAPI_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..d6b3cd7 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 1116923..9c615f7 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
  * ----------------
  */
 #define AMTYPE_INDEX					'i'		/* index access method */
+#define	AMTYPE_SEQUENCE					'S'		/* sequence access method */
 
 /* ----------------
  *		initial contents of pg_am
@@ -84,4 +85,8 @@ DATA(insert OID = 3580 (  brin		brinhandler i ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
+DATA(insert OID = 6023 (  local		seqam_local_handler	S ));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6023
+
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a595327..c8f86f2 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1759,6 +1759,10 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v u 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 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 25 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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 s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3690,6 +3694,10 @@ DATA(insert OID = 326  (  index_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f
 DESCR("I/O");
 DATA(insert OID = 327  (  index_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
 DESCR("I/O");
+DATA(insert OID = 6021  (  seq_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 6020 "2275" _null_ _null_ _null_ _null_ _null_ seq_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6022 (  seq_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6020" _null_ _null_ _null_ _null_ _null_ seq_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
 DATA(insert OID = 3311 (  tsm_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 3312 (  tsm_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
@@ -3701,6 +3709,10 @@ DESCR("BERNOULLI tablesample method handler");
 DATA(insert OID = 3314 (  system			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_system_handler _null_ _null_ _null_ ));
 DESCR("SYSTEM tablesample method handler");
 
+/* sequence access method handlers */
+DATA(insert OID = 6024 (  seqam_local_handler	   PGNSP PGUID 12 1 0 0 0 f f f f f f v s 1 0 6020 "2281" _null_ _null_ _null_ _null_ _null_ seqam_local_handler _null_ _null_ _null_ ));
+DESCR("Local SequenceAM handler");
+
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
 DESCR("MD5 hash");
@@ -5259,6 +5271,13 @@ DESCR("pg_controldata recovery state information as a function");
 DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
 DESCR("pg_controldata init state information as a function");
 
+/* Sequence AM */
+DATA(insert OID = 6027 (  seqam_local_state_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 6025 "2275" _null_ _null_ _null_ _null_ _null_ seqam_local_state_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6028 (  seqam_local_state_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6025" _null_ _null_ _null_ _null_ _null_ seqam_local_state_out _null_ _null_ _null_ ));
+DESCR("I/O");
+
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2c90b76..40214e3 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -696,11 +696,18 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 #define FDW_HANDLEROID	3115
 DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define INDEX_AM_HANDLEROID	325
+DATA(insert OID = 6020 ( seq_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 seq_am_handler_in seq_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQ_AM_HANDLEROID	6020
 DATA(insert OID = 3310 ( tsm_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define TSM_HANDLEROID	3310
 DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 #define ANYRANGEOID		3831
 
+/* Sequence AM composite types */
+DATA(insert OID = 6025 ( seqam_local_state	PGNSP PGUID 16 f b U f t \054 0 0 6026 seqam_local_state_in seqam_local_state_out - - - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQAMLOCALSTATEOID 6025
+DATA(insert OID = 6026 ( _seqam_local_state	PGNSP PGUID -1 f b A f t \054 0 6025 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 
 /*
  * macros
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index b064eb4..c9bf431 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -139,6 +139,7 @@ extern Datum transformGenericOptions(Oid catalogId,
 extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
 extern void RemoveAccessMethodById(Oid amOid);
 extern Oid	get_index_am_oid(const char *amname, bool missing_ok);
+extern Oid	get_seq_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);
 
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..e0f6439 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -19,20 +19,18 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "storage/relfilenode.h"
-
+#include "access/htup_details.h"
 
 typedef struct FormData_pg_sequence
 {
-	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
+	/* amstate follows */
+	char		amstate[FLEXIBLE_ARRAY_MEMBER];
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,19 +39,16 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  * Columns of a sequence relation
  */
 
-#define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_STARTVAL		1
+#define SEQ_COL_INCBY			2
+#define SEQ_COL_MAXVALUE		3
+#define SEQ_COL_MINVALUE		4
+#define SEQ_COL_CACHE			5
+#define SEQ_COL_CYCLE			6
+#define SEQ_COL_AMSTATE			7
 
-#define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_FIRSTCOL		SEQ_COL_STARTVAL
+#define SEQ_COL_LASTCOL			SEQ_COL_AMSTATE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +67,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 7a770f4..1bceec8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 734df77..752d3a1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -477,6 +477,7 @@ typedef enum NodeTag
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
+	T_SeqAmRoutine,				/* in access/seqamapi.h */
 	T_TsmRoutine				/* in access/tsmapi.h */
 } NodeTag;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8b958b4..859fbad 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2205,8 +2205,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2214,8 +2216,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 206288d..1c89b49 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -584,6 +584,8 @@ extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
 extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_in(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_out(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
 extern Datum internal_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f2bebf2..4fe7e52 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -106,13 +106,18 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	Oid			rd_amhandler;	/* OID of access method's handler function */
+
+	/* These are non-NULL only for a sequence relation */
+	struct SeqAmRoutine *rd_seqamroutine;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
@@ -126,7 +131,6 @@ typedef struct RelationData
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
-	Oid			rd_amhandler;	/* OID of index AM's handler function */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
 	/* use "struct" here to avoid needing to include amapi.h: */
 	struct IndexAmRoutine *rd_amroutine;		/* index AM's API struct */
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index 47d6024..9242982 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -106,3 +106,22 @@ drop cascades to index grect2ind
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+-- Checks for sequence access methods
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+ERROR:  cannot drop access method local2 because other objects depend on it
+DETAIL:  sequence test_seqam depends on access method local2
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ae50c94..f1b1fc2 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -129,8 +129,9 @@ NOTICE:  view "v12_temp" will be a temporary view
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 NOTICE:  view "v13_temp" will be a temporary view
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 7c09fa3..ab0d299 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1586,7 +1586,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
  oid | amname | oid | proname 
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3a19cde 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate 
+-------------+--------------+---------------------+-----------+-------------+-----------+---------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (1,f,0)
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate  
+-------------+--------------+---------------------+-----------+-------------+-----------+----------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (2,t,31)
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c5dfbb5..a30582e 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,52 @@ SELECT table_name, column_name, is_updatable
   FROM information_schema.columns
  WHERE table_name LIKE E'r_\\_view%'
  ORDER BY table_name, ordinal_position;
- table_name |  column_name  | is_updatable 
-------------+---------------+--------------
- ro_view1   | a             | NO
- ro_view1   | b             | NO
- ro_view10  | a             | NO
- ro_view11  | a             | NO
- ro_view11  | b             | NO
- ro_view12  | a             | NO
- ro_view13  | a             | NO
- ro_view13  | b             | NO
- ro_view17  | a             | NO
- ro_view17  | b             | NO
- ro_view18  | a             | NO
- ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
- ro_view19  | start_value   | NO
- ro_view19  | increment_by  | NO
- ro_view19  | max_value     | NO
- ro_view19  | min_value     | NO
- ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
- ro_view19  | is_cycled     | NO
- ro_view19  | is_called     | NO
- ro_view2   | a             | NO
- ro_view2   | b             | NO
- ro_view20  | a             | NO
- ro_view20  | b             | NO
- ro_view20  | g             | NO
- ro_view3   | ?column?      | NO
- ro_view4   | count         | NO
- ro_view5   | a             | NO
- ro_view5   | rank          | NO
- ro_view6   | a             | NO
- ro_view6   | b             | NO
- ro_view7   | a             | NO
- ro_view7   | b             | NO
- ro_view8   | a             | NO
- ro_view8   | b             | NO
- ro_view9   | a             | NO
- ro_view9   | b             | NO
- rw_view14  | ctid          | NO
- rw_view14  | a             | YES
- rw_view14  | b             | YES
- rw_view15  | a             | YES
- rw_view15  | upper         | NO
- rw_view16  | a             | YES
- rw_view16  | b             | YES
- rw_view16  | aa            | YES
-(46 rows)
+ table_name | column_name  | is_updatable 
+------------+--------------+--------------
+ ro_view1   | a            | NO
+ ro_view1   | b            | NO
+ ro_view10  | a            | NO
+ ro_view11  | a            | NO
+ ro_view11  | b            | NO
+ ro_view12  | a            | NO
+ ro_view13  | a            | NO
+ ro_view13  | b            | NO
+ ro_view17  | a            | NO
+ ro_view17  | b            | NO
+ ro_view18  | a            | NO
+ ro_view19  | start_value  | NO
+ ro_view19  | increment_by | NO
+ ro_view19  | max_value    | NO
+ ro_view19  | min_value    | NO
+ ro_view19  | cache_value  | NO
+ ro_view19  | is_cycled    | NO
+ ro_view19  | amstate      | NO
+ ro_view2   | a            | NO
+ ro_view2   | b            | NO
+ ro_view20  | a            | NO
+ ro_view20  | b            | NO
+ ro_view20  | g            | NO
+ ro_view3   | ?column?     | NO
+ ro_view4   | count        | NO
+ ro_view5   | a            | NO
+ ro_view5   | rank         | NO
+ ro_view6   | a            | NO
+ ro_view6   | b            | NO
+ ro_view7   | a            | NO
+ ro_view7   | b            | NO
+ ro_view8   | a            | NO
+ ro_view8   | b            | NO
+ ro_view9   | a            | NO
+ ro_view9   | b            | NO
+ rw_view14  | ctid         | NO
+ rw_view14  | a            | YES
+ rw_view14  | b            | YES
+ rw_view15  | a            | YES
+ rw_view15  | upper        | NO
+ rw_view16  | a            | YES
+ rw_view16  | b            | YES
+ rw_view16  | aa           | YES
+(43 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index e2051c5..e811ea3 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -71,3 +71,19 @@ DROP ACCESS METHOD gist2 CASCADE;
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+
+-- Checks for sequence access methods
+
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 58d361d..e967fff 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -131,8 +131,9 @@ CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 6c9784a..6c44d70 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1055,7 +1055,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
 
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..c2b1164 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
-- 
1.9.1

#114Fabrízio de Royes Mello
fabriziomello@gmail.com
In reply to: Petr Jelinek (#113)
Re: Sequence Access Method WIP

On Tue, Mar 29, 2016 at 2:26 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

On 29/03/16 18:50, Fabrízio de Royes Mello wrote:

On Tue, Mar 29, 2016 at 12:25 PM, David Steele <david@pgmasters.net
<mailto:david@pgmasters.net>> wrote:

Hi Petr,

On 3/28/16 3:11 PM, Fabrízio de Royes Mello wrote:

fabrizio@bagual:~/pgsql
$ bin/pg_dump > /tmp/fabrizio.sql
pg_dump: [archiver (db)] query failed: ERROR: column "sequence_name"
does not exist
LINE 1: SELECT sequence_name, start_value, increment_by, CASE WHEN

i...

^
pg_dump: [archiver (db)] query was: SELECT sequence_name,

start_value,

increment_by, CASE WHEN increment_by > 0 AND max_value =
9223372036854775807 THEN NULL WHEN increment_by < 0 AND

max_value =

-1 THEN NULL ELSE max_value END AS max_value, CASE WHEN
increment_by > 0 AND min_value = 1 THEN NULL WHEN increment_by

< 0

AND min_value = -9223372036854775807 THEN NULL ELSE min_value

END

AS min_value, cache_value, is_cycled FROM x

It looks like a new patch is needed. I've marked this "waiting on

author".

Yeah there were some incompatible commits since my last rebase, fixed,

along with the pg_dump bugs..

Now all applies without errors, build and "make check" too.

But there are other issue in the gapless-seq extension when I ran "make
check":

43 CREATE EXTENSION gapless_seq;
44 CREATE SEQUENCE test_gapless USING gapless;
45 SELECT nextval('test_gapless'::regclass);
46 ! ERROR: could not access status of transaction 1275068416
47 ! DETAIL: Could not open file "pg_subtrans/4C00": No such file or
directory.
48 BEGIN;
49 SELECT nextval('test_gapless'::regclass);
50 ! ERROR: could not access status of transaction 1275068416
51 ! DETAIL: Could not open file "pg_subtrans/4C00": No such file or
directory.
52 SELECT nextval('test_gapless'::regclass);
53 ! ERROR: current transaction is aborted, commands ignored until
end of transaction block
54 SELECT nextval('test_gapless'::regclass);
55 ! ERROR: current transaction is aborted, commands ignored until
end of transaction block
56 ROLLBACK;
57 SELECT nextval('test_gapless'::regclass);
58 ! ERROR: could not access status of transaction 1275068416
59 ! DETAIL: Could not open file "pg_subtrans/4C00": No such file or
directory.

And I see the same running manually:

fabrizio=# create extension gapless_seq;
CREATE EXTENSION
fabrizio=# create sequence x using gapless;
CREATE SEQUENCE
fabrizio=# select nextval('x');
ERROR: could not access status of transaction 1258291200
DETAIL: Could not open file "pg_subtrans/4B00": No such file or

directory.

Regards,

Hmm I am unable to reproduce this. What OS? Any special configure flags

you use?

In my environment the error remains with your last patches.

I didn't use any special.

./configure --prefix=/home/fabrizio/pgsql --enable-cassert
--enable-coverage --enable-tap-tests --enable-depend
make -s -j8 install
make check-world

My environment:

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ uname -a
Linux bagual 3.13.0-83-generic #127-Ubuntu SMP Fri Mar 11 00:25:37 UTC 2016
x86_64 x86_64 x86_64 GNU/Linux

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Regards,

--
Fabrízio de Royes Mello
Consultoria/Coaching PostgreSQL

Show quoted text

Timbira: http://www.timbira.com.br
Blog: http://fabriziomello.github.io
Linkedin: http://br.linkedin.com/in/fabriziomello
Twitter: http://twitter.com/fabriziomello
Github: http://github.com/fabriziomello

#115Petr Jelinek
petr@2ndquadrant.com
In reply to: Fabrízio de Royes Mello (#114)
1 attachment(s)
Re: Sequence Access Method WIP

On 29/03/16 19:46, Fabrízio de Royes Mello wrotez

Hmm I am unable to reproduce this. What OS? Any special configure

flags you use?

In my environment the error remains with your last patches.

I didn't use any special.

./configure --prefix=/home/fabrizio/pgsql --enable-cassert
--enable-coverage --enable-tap-tests --enable-depend
make -s -j8 install
make check-world

My environment:

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ uname -a
Linux bagual 3.13.0-83-generic #127-Ubuntu SMP Fri Mar 11 00:25:37 UTC
2016 x86_64 x86_64 x86_64 GNU/Linux

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Hmm nothing special indeed, still can't reproduce, I did one blind try
for a fix. Can you test with attached?

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0002-gapless-seq-2016-03-29-2.patchtext/x-diff; name=0002-gapless-seq-2016-03-29-2.patchDownload
From 7f42f2b420e2b931e1ca013f3fdeaccf302f3618 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Fri, 4 Mar 2016 15:03:44 +0100
Subject: [PATCH 2/2] gapless seq

---
 contrib/Makefile                             |   1 +
 contrib/gapless_seq/Makefile                 |  63 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  57 +++
 contrib/gapless_seq/gapless_seq.c            | 530 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 +++
 doc/src/sgml/contrib.sgml                    |   1 +
 doc/src/sgml/filelist.sgml                   |   1 +
 doc/src/sgml/gapless-seq.sgml                |  24 ++
 12 files changed, 949 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql
 create mode 100644 doc/src/sgml/gapless-seq.sgml

diff --git a/contrib/Makefile b/contrib/Makefile
index d12dd63..d2a0620 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
 		earthdistance	\
 		file_fdw	\
 		fuzzystrmatch	\
+		gapless_seq	\
 		hstore		\
 		intagg		\
 		intarray	\
diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..130104a
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+ERROR:  cannot drop access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..3b41f86
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,57 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_handler(internal)
+RETURNS SEQ_AM_HANDLER
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_in(cstring)
+RETURNS seqam_gapless_state
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_out(seqam_gapless_state)
+RETURNS cstring
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TYPE seqam_gapless_state (
+	INPUT   = seqam_gapless_state_in,
+	OUTPUT  = seqam_gapless_state_out,
+	INTERNALLENGTH = 16
+);
+COMMENT ON TYPE seqam_gapless_state IS 'state for gapless sequence am';
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	last_value bigint NOT NULL,
+	is_called bool NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relkind = 'S' AND relam = (
+			SELECT oid FROM pg_am WHERE amname = 'gapless' AND amtype = 'S'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD gapless TYPE SEQUENCE HANDLER seqam_gapless_handler;
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..5fd84f9
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,530 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager / Gapless sequence implementation
+ *
+ *------------------------------------------------------------
+ */
+
+typedef struct GaplessSequenceState
+{
+	int64   last_value;
+	uint32	xid;
+	bool	is_called;
+} GaplessSequenceState;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	int64	last_value;
+	bool	is_called;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static bytea *seqam_gapless_reloptions(Datum reloptions, bool validate);
+static Datum seqam_gapless_init(Relation seqrel, Form_pg_sequence seq,
+								int64 restart_value, bool restart_requested,
+								bool is_init);
+static int64 seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh,
+								 int64 nrequested, int64 *last);
+static void seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh,
+								 int64 new_value);
+static Datum seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+									Datum amstate);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_handler);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_in);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_out);
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+							   int64 last_value, bool is_called);
+
+Datum
+seqam_gapless_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr, *end;
+	Datum		d;
+	GaplessSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	end = ptr;
+	while (*end != ',' && *end != ')')
+	{
+		char		ch = *end++;
+
+		if (ch == '\0')
+			goto malformed;
+	}
+
+	d = DirectFunctionCall1(int8in,
+							CStringGetDatum(pnstrdup(ptr, end-ptr)));
+	state->last_value = DatumGetInt64(d);
+
+	/* is_called is optional */
+	if (*end == ',')
+	{
+		ptr = ++end;
+
+		end = state_str+strlen(state_str)-1;
+
+		if (*end != ')')
+			goto malformed;
+
+		d = DirectFunctionCall1(boolin,
+								CStringGetDatum(pnstrdup(ptr, end-ptr)));
+		state->is_called = DatumGetBool(d);
+	}
+	else
+		state->is_called = true;
+
+	state->xid = InvalidTransactionId;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_gapless_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_gapless_state_out(PG_FUNCTION_ARGS)
+{
+	GaplessSequenceState   *state;
+	StringInfoData			buf;
+
+	state = (GaplessSequenceState *) PG_GETARG_POINTER(0);
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("UINT64_FORMAT",%s)",
+					 state->last_value, state->is_called ? "t" : "f");
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the gapless sequence access method.
+ */
+Datum
+seqam_gapless_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine   *seqam = makeNode(SeqAmRoutine);
+	Oid				nspid,
+					typid;
+
+	nspid = get_namespace_oid("gapless_seq", false);
+	typid = GetSysCacheOid2(TYPENAMENSP,
+							PointerGetDatum("seqam_gapless_state"),
+							ObjectIdGetDatum(nspid));
+
+	if (!OidIsValid(typid))
+		elog(ERROR, "cache lookup failed for type \"seqam_gapless_state\"");
+
+	seqam->StateTypeOid = typid;
+	seqam->amoptions = seqam_gapless_reloptions;
+	seqam->Init = seqam_gapless_init;
+	seqam->Alloc = seqam_gapless_alloc;
+	seqam->Setval = seqam_gapless_setval;
+	seqam->GetState = seqam_gapless_get_state;
+	seqam->SetState = seqam_gapless_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+static bytea *
+seqam_gapless_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+static Datum
+seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	Oid			seqrelid = seqrel->rd_id;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	GaplessSequenceState   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(GaplessSequenceState));
+	else
+		seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->xid = GetTopTransactionId();
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, valtuple, seqstate->last_value,
+						   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+static int64
+seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+					int64 *last)
+{
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	Form_pg_sequence		seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, result, true);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* We always WAL log for gapless sequence. */
+	sequence_start_update(seqh, true);
+	seqstate->last_value = result;
+	seqstate->is_called = true;
+	if (seqstate->xid != local_xid)
+		seqstate->xid = local_xid;
+	sequence_save_state(seqh, PointerGetDatum(seqstate), true);
+	sequence_finish_update(seqh);
+
+	*last = result;
+
+	return result;
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+static void
+seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+static Datum
+seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	GaplessSequenceState   *seqstate = palloc0(sizeof(GaplessSequenceState));
+
+	/*
+	 * Get the last value from the values table, if not found use the values
+	 * from sequence typle.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		seqstate->last_value = v->last_value;
+		seqstate->is_called = v->is_called;
+	}
+	else
+	{
+		Datum		amstate = sequence_read_state(seqh);
+
+		memcpy(seqstate, DatumGetPointer(amstate),
+			   sizeof(GaplessSequenceState));
+		sequence_release_tuple(seqh);
+	}
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+static void
+seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+						Datum amstate)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	FormData_pg_sequence   *seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(seqstate->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, seqstate->last_value,
+					   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	FormData_pg_sequence   *seq = sequence_read_options(seqh);
+	GaplessSequenceState   *seqstate = (GaplessSequenceState *) seq->amstate;
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (seqstate->xid != local_xid &&
+		   TransactionIdIsInProgress(seqstate->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seqstate->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seq = sequence_read_options(seqh);
+		seqstate = (GaplessSequenceState *) seq->amstate;
+	}
+
+	return seq;
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+				   int64 last_value, bool is_called)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..b49cec6
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index 4e3f337..9852933 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -116,6 +116,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
  &earthdistance;
  &file-fdw;
  &fuzzystrmatch;
+ &gapless-seq;
  &hstore;
  &intagg;
  &intarray;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index fd355dc..2681437 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -119,6 +119,7 @@
 <!ENTITY earthdistance   SYSTEM "earthdistance.sgml">
 <!ENTITY file-fdw        SYSTEM "file-fdw.sgml">
 <!ENTITY fuzzystrmatch   SYSTEM "fuzzystrmatch.sgml">
+<!ENTITY gapless-seq     SYSTEM "gapless-seq.sgml">
 <!ENTITY hstore          SYSTEM "hstore.sgml">
 <!ENTITY intagg          SYSTEM "intagg.sgml">
 <!ENTITY intarray        SYSTEM "intarray.sgml">
diff --git a/doc/src/sgml/gapless-seq.sgml b/doc/src/sgml/gapless-seq.sgml
new file mode 100644
index 0000000..44cc224
--- /dev/null
+++ b/doc/src/sgml/gapless-seq.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless-seq" xreflabel="gapless_seq">
+ <title>gapless_seq</title>
+
+ <indexterm zone="gapless-seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence and hold
+  lock on the sequence itself, so any concurrent nextval() calls have to wait
+  for the transaction to finish. This could be very costly and is not
+  recommended for general usage except in specific applications that require
+  this feature.
+ </para>
+
+</sect1>
-- 
1.9.1

#116Fabrízio de Royes Mello
fabriziomello@gmail.com
In reply to: Petr Jelinek (#115)
Re: Sequence Access Method WIP

On Tue, Mar 29, 2016 at 4:59 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

On 29/03/16 19:46, Fabrízio de Royes Mello wrotez

Hmm I am unable to reproduce this. What OS? Any special configure

flags you use?

In my environment the error remains with your last patches.

I didn't use any special.

./configure --prefix=/home/fabrizio/pgsql --enable-cassert
--enable-coverage --enable-tap-tests --enable-depend
make -s -j8 install
make check-world

My environment:

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ uname -a
Linux bagual 3.13.0-83-generic #127-Ubuntu SMP Fri Mar 11 00:25:37 UTC
2016 x86_64 x86_64 x86_64 GNU/Linux

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is

NO

warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR

PURPOSE.

Hmm nothing special indeed, still can't reproduce, I did one blind try

for a fix. Can you test with attached?

Same error... Now I'll leave in a trip but when I arrive I'll try to figure
out what happening debugging the code.

Regards,

--
Fabrízio de Royes Mello
Consultoria/Coaching PostgreSQL

Show quoted text

Timbira: http://www.timbira.com.br
Blog: http://fabriziomello.github.io
Linkedin: http://br.linkedin.com/in/fabriziomello
Twitter: http://twitter.com/fabriziomello
Github: http://github.com/fabriziomello

#117Petr Jelinek
petr@2ndquadrant.com
In reply to: Fabrízio de Royes Mello (#116)
Re: Sequence Access Method WIP

On 29/03/16 22:08, Fabrízio de Royes Mello wrote:

On Tue, Mar 29, 2016 at 4:59 PM, Petr Jelinek <petr@2ndquadrant.com
<mailto:petr@2ndquadrant.com>> wrote:

On 29/03/16 19:46, Fabrízio de Royes Mello wrotez

Hmm I am unable to reproduce this. What OS? Any special configure

flags you use?

In my environment the error remains with your last patches.

I didn't use any special.

./configure --prefix=/home/fabrizio/pgsql --enable-cassert
--enable-coverage --enable-tap-tests --enable-depend
make -s -j8 install
make check-world

My environment:

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ uname -a
Linux bagual 3.13.0-83-generic #127-Ubuntu SMP Fri Mar 11 00:25:37 UTC
2016 x86_64 x86_64 x86_64 GNU/Linux

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There

is NO

warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR

PURPOSE.

Hmm nothing special indeed, still can't reproduce, I did one blind

try for a fix. Can you test with attached?

Same error... Now I'll leave in a trip but when I arrive I'll try to
figure out what happening debugging the code.

Okay, thanks.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#118Fabrízio de Royes Mello
fabriziomello@gmail.com
In reply to: Petr Jelinek (#117)
Re: Sequence Access Method WIP

On Tue, Mar 29, 2016 at 5:58 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

On 29/03/16 22:08, Fabrízio de Royes Mello wrote:

On Tue, Mar 29, 2016 at 4:59 PM, Petr Jelinek <petr@2ndquadrant.com
<mailto:petr@2ndquadrant.com>> wrote:

On 29/03/16 19:46, Fabrízio de Royes Mello wrotez

Hmm I am unable to reproduce this. What OS? Any special configure

flags you use?

In my environment the error remains with your last patches.

I didn't use any special.

./configure --prefix=/home/fabrizio/pgsql --enable-cassert
--enable-coverage --enable-tap-tests --enable-depend
make -s -j8 install
make check-world

My environment:

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ uname -a
Linux bagual 3.13.0-83-generic #127-Ubuntu SMP Fri Mar 11 00:25:37

UTC

2016 x86_64 x86_64 x86_64 GNU/Linux

fabrizio@bagual:/d/postgresql (0002-gapless-seq-petr)
$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There

is NO

warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR

PURPOSE.

Hmm nothing special indeed, still can't reproduce, I did one blind

try for a fix. Can you test with attached?

Same error... Now I'll leave in a trip but when I arrive I'll try to
figure out what happening debugging the code.

Okay, thanks.

Hi,

After some debugging seems that the "seqstate->xid" in "wait_for_sequence"
isn't properly initialized or initialized with the wrong value (1258291200).

Regards,

--
Fabrízio de Royes Mello
Consultoria/Coaching PostgreSQL

Show quoted text

Timbira: http://www.timbira.com.br
Blog: http://fabriziomello.github.io
Linkedin: http://br.linkedin.com/in/fabriziomello
Twitter: http://twitter.com/fabriziomello
Github: http://github.com/fabriziomello

#119Jose Luis Tallon
jltallon@adv-solutions.net
In reply to: Fabrízio de Royes Mello (#118)
Re: Sequence Access Method WIP

The following review has been posted through the commitfest application:
make installcheck-world: not tested
Implements feature: not tested
Spec compliant: not tested
Documentation: not tested

[Partial review] Evaluated: 0002-gapless-seq-2016-03-29-2.patch
Needs updating code copyright years ... or is this really from 2013? [ contrib/gapless_seq/gapless_seq.c ]
Patch applies cleanly to current master (3063e7a84026ced2aadd2262f75eebbe6240f85b)
It does compile cleanly.

DESIGN
The decision to hardcode the schema GAPLESS_SEQ_NAMESPACE ("gapless_seq") and VALUES_TABLE_NAME ("seqam_gapless_values") strikes me a bit: while I understand the creation of a private schema named after the extension, it seems overkill for just a single table.
I would suggest to devote some reserved schema name for internal implementation details and/or AM implementation details, if deemed reasonable.
On the other hand, creating the schema/table upon extension installation makes the values table use the default tablespace for the database, which can be good for concurrency --- direct writes to less loaded storage
(Note that users may want to move this table into an SSD-backed tablespace or equivalently fast storage ... moreso when many --not just one-- gapless sequences are being used)

Yet, there is <20141203102425.GT2456@alap3.anarazel.de> where Andres argues against anything different than one-page-per-sequence implementations.
I guess this patch changes everything in this respect.

CODE
seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
bool restart_requested, bool is_init)
-> is_init sounds confusing; suggest renaming to "initialize" or "initial" to avoid reading as "is_initIALIZED"

DEPENDS ON 0001-seqam-v10.patch , which isn't commited yet --- and it doesn't apply cleanly to current git master.
Please update/rebase the patch and resubmit.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#120Petr Jelinek
petr@2ndquadrant.com
In reply to: Jose Luis Tallon (#119)
Re: Sequence Access Method WIP

Hi,

Thanks for review.

On 30/03/16 15:22, Jose Luis Tallon wrote:

[Partial review] Evaluated: 0002-gapless-seq-2016-03-29-2.patch
Needs updating code copyright years ... or is this really from 2013? [ contrib/gapless_seq/gapless_seq.c ]
Patch applies cleanly to current master (3063e7a84026ced2aadd2262f75eebbe6240f85b)

Ouch good point, it does show how long the whole sequence am thing has
been around.

DESIGN
The decision to hardcode the schema GAPLESS_SEQ_NAMESPACE ("gapless_seq") and VALUES_TABLE_NAME ("seqam_gapless_values") strikes me a bit: while I understand the creation of a private schema named after the extension, it seems overkill for just a single table.
I would suggest to devote some reserved schema name for internal implementation details and/or AM implementation details, if deemed reasonable.
On the other hand, creating the schema/table upon extension installation makes the values table use the default tablespace for the database, which can be good for concurrency --- direct writes to less loaded storage
(Note that users may want to move this table into an SSD-backed tablespace or equivalently fast storage ... moreso when many --not just one-- gapless sequences are being used)

Schema is needed for the handler function as well. In general I don't
want to add another internal schema that will be empty when no sequence
AM is installed.

Yet, there is <20141203102425.GT2456@alap3.anarazel.de> where Andres argues against anything different than one-page-per-sequence implementations.
I guess this patch changes everything in this respect.

Not really, gapless just needs table for transactionality and as such is
an exception. It does not change how the nontransactional sequence
storage works though. I agree with Andres on this one.

CODE
seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
bool restart_requested, bool is_init)
-> is_init sounds confusing; suggest renaming to "initialize" or "initial" to avoid reading as "is_initIALIZED"

Sounds good.

DEPENDS ON 0001-seqam-v10.patch , which isn't commited yet --- and it doesn't apply cleanly to current git master.
Please update/rebase the patch and resubmit.

The current version of seqam is 0001-seqam-2016-03-29 which should apply
correctly.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#121Petr Jelinek
petr@2ndquadrant.com
In reply to: Fabrízio de Royes Mello (#118)
2 attachment(s)
Re: Sequence Access Method WIP

Hi,

new version attached that should fix the issue. It was alignment -
honestly don't know what I was thinking using fixed alignment when the
AMs can define their own type.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0002-gapless-seq-2016-03-31.patchtext/x-diff; name=0002-gapless-seq-2016-03-31.patchDownload
From a8f3202b371572bbb4f6e8c6ed1a840f1f6d01ab Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Fri, 4 Mar 2016 15:03:44 +0100
Subject: [PATCH 2/2] gapless seq

---
 contrib/Makefile                             |   1 +
 contrib/gapless_seq/Makefile                 |  63 ++++
 contrib/gapless_seq/expected/concurrency.out |  31 ++
 contrib/gapless_seq/expected/gapless_seq.out | 145 ++++++++
 contrib/gapless_seq/gapless_seq--1.0.sql     |  58 +++
 contrib/gapless_seq/gapless_seq.c            | 538 +++++++++++++++++++++++++++
 contrib/gapless_seq/gapless_seq.control      |   6 +
 contrib/gapless_seq/specs/concurrency.spec   |  29 ++
 contrib/gapless_seq/sql/gapless_seq.sql      |  61 +++
 doc/src/sgml/contrib.sgml                    |   1 +
 doc/src/sgml/filelist.sgml                   |   1 +
 doc/src/sgml/gapless-seq.sgml                |  24 ++
 12 files changed, 958 insertions(+)
 create mode 100644 contrib/gapless_seq/Makefile
 create mode 100644 contrib/gapless_seq/expected/concurrency.out
 create mode 100644 contrib/gapless_seq/expected/gapless_seq.out
 create mode 100644 contrib/gapless_seq/gapless_seq--1.0.sql
 create mode 100644 contrib/gapless_seq/gapless_seq.c
 create mode 100644 contrib/gapless_seq/gapless_seq.control
 create mode 100644 contrib/gapless_seq/specs/concurrency.spec
 create mode 100644 contrib/gapless_seq/sql/gapless_seq.sql
 create mode 100644 doc/src/sgml/gapless-seq.sgml

diff --git a/contrib/Makefile b/contrib/Makefile
index d12dd63..d2a0620 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
 		earthdistance	\
 		file_fdw	\
 		fuzzystrmatch	\
+		gapless_seq	\
 		hstore		\
 		intagg		\
 		intarray	\
diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..130104a
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+ERROR:  cannot drop access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..99d6021
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,58 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_handler(internal)
+RETURNS SEQ_AM_HANDLER
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_in(cstring)
+RETURNS seqam_gapless_state
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_out(seqam_gapless_state)
+RETURNS cstring
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TYPE seqam_gapless_state (
+	INPUT   = seqam_gapless_state_in,
+	OUTPUT  = seqam_gapless_state_out,
+	INTERNALLENGTH = 16,
+	ALIGNMENT = double
+);
+COMMENT ON TYPE seqam_gapless_state IS 'state for gapless sequence am';
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	last_value bigint NOT NULL,
+	is_called bool NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relkind = 'S' AND relam = (
+			SELECT oid FROM pg_am WHERE amname = 'gapless' AND amtype = 'S'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD gapless TYPE SEQUENCE HANDLER seqam_gapless_handler;
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..6a97494
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,538 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager / Gapless sequence implementation
+ *
+ *------------------------------------------------------------
+ */
+
+typedef struct GaplessSequenceState
+{
+	int64   last_value;
+	uint32	xid;
+	bool	is_called;
+} GaplessSequenceState;
+
+typedef struct GaplessSequence
+{
+	FormData_pg_sequence	seq;
+	GaplessSequenceState	state;
+} GaplessSequence;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	int64	last_value;
+	bool	is_called;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static bytea *seqam_gapless_reloptions(Datum reloptions, bool validate);
+static Datum seqam_gapless_init(Relation seqrel, Form_pg_sequence seq,
+								int64 restart_value, bool restart_requested,
+								bool is_init);
+static int64 seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh,
+								 int64 nrequested, int64 *last);
+static void seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh,
+								 int64 new_value);
+static Datum seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+									Datum amstate);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_handler);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_in);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_out);
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+							   int64 last_value, bool is_called);
+
+Datum
+seqam_gapless_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr, *end;
+	Datum		d;
+	GaplessSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	end = ptr;
+	while (*end != ',' && *end != ')')
+	{
+		char		ch = *end++;
+
+		if (ch == '\0')
+			goto malformed;
+	}
+
+	d = DirectFunctionCall1(int8in,
+							CStringGetDatum(pnstrdup(ptr, end-ptr)));
+	state->last_value = DatumGetInt64(d);
+
+	/* is_called is optional */
+	if (*end == ',')
+	{
+		ptr = ++end;
+
+		end = state_str+strlen(state_str)-1;
+
+		if (*end != ')')
+			goto malformed;
+
+		d = DirectFunctionCall1(boolin,
+								CStringGetDatum(pnstrdup(ptr, end-ptr)));
+		state->is_called = DatumGetBool(d);
+	}
+	else
+		state->is_called = true;
+
+	state->xid = InvalidTransactionId;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_gapless_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_gapless_state_out(PG_FUNCTION_ARGS)
+{
+	GaplessSequenceState   *state;
+	StringInfoData			buf;
+
+	state = (GaplessSequenceState *) PG_GETARG_POINTER(0);
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("INT64_FORMAT",%s)",
+					 state->last_value, state->is_called ? "t" : "f");
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the gapless sequence access method.
+ */
+Datum
+seqam_gapless_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine   *seqam = makeNode(SeqAmRoutine);
+	Oid				nspid,
+					typid;
+
+	nspid = get_namespace_oid("gapless_seq", false);
+	typid = GetSysCacheOid2(TYPENAMENSP,
+							PointerGetDatum("seqam_gapless_state"),
+							ObjectIdGetDatum(nspid));
+
+	if (!OidIsValid(typid))
+		elog(ERROR, "cache lookup failed for type \"seqam_gapless_state\"");
+
+	seqam->StateTypeOid = typid;
+	seqam->amoptions = seqam_gapless_reloptions;
+	seqam->Init = seqam_gapless_init;
+	seqam->Alloc = seqam_gapless_alloc;
+	seqam->Setval = seqam_gapless_setval;
+	seqam->GetState = seqam_gapless_get_state;
+	seqam->SetState = seqam_gapless_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+static bytea *
+seqam_gapless_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+static Datum
+seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	Oid			seqrelid = seqrel->rd_id;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	GaplessSequenceState   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(GaplessSequenceState));
+	else
+		seqstate = &((GaplessSequence *) seq)->state;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->xid = GetTopTransactionId();
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, valtuple, seqstate->last_value,
+						   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+static int64
+seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+					int64 *last)
+{
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	Form_pg_sequence		seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = &((GaplessSequence *) seq)->state;
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, result, true);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* We always WAL log for gapless sequence. */
+	sequence_start_update(seqh, true);
+	seqstate->last_value = result;
+	seqstate->is_called = true;
+	if (seqstate->xid != local_xid)
+		seqstate->xid = local_xid;
+	sequence_save_state(seqh, PointerGetDatum(seqstate), true);
+	sequence_finish_update(seqh);
+
+	*last = result;
+
+	return result;
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+static void
+seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+static Datum
+seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	GaplessSequenceState   *seqstate = palloc0(sizeof(GaplessSequenceState));
+
+	/*
+	 * Get the last value from the values table, if not found use the values
+	 * from sequence typle.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		seqstate->last_value = v->last_value;
+		seqstate->is_called = v->is_called;
+	}
+	else
+	{
+		Datum		amstate = sequence_read_state(seqh);
+
+		memcpy(seqstate, DatumGetPointer(amstate),
+			   sizeof(GaplessSequenceState));
+		sequence_release_tuple(seqh);
+	}
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+static void
+seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+						Datum amstate)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	FormData_pg_sequence   *seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(seqstate->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, seqstate->last_value,
+					   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	GaplessSequenceState   *seqstate;
+	Datum					seqstated = sequence_read_state(seqh);
+
+	seqstate = (GaplessSequenceState *) DatumGetPointer(seqstated);
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (seqstate->xid != local_xid &&
+		   TransactionIdIsInProgress(seqstate->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seqstate->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seqstated = sequence_read_state(seqh);
+		seqstate = (GaplessSequenceState *) DatumGetPointer(seqstated);
+	}
+
+	return sequence_read_options(seqh);
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+				   int64 last_value, bool is_called)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..b49cec6
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index 4e3f337..9852933 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -116,6 +116,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
  &earthdistance;
  &file-fdw;
  &fuzzystrmatch;
+ &gapless-seq;
  &hstore;
  &intagg;
  &intarray;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index fd355dc..2681437 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -119,6 +119,7 @@
 <!ENTITY earthdistance   SYSTEM "earthdistance.sgml">
 <!ENTITY file-fdw        SYSTEM "file-fdw.sgml">
 <!ENTITY fuzzystrmatch   SYSTEM "fuzzystrmatch.sgml">
+<!ENTITY gapless-seq     SYSTEM "gapless-seq.sgml">
 <!ENTITY hstore          SYSTEM "hstore.sgml">
 <!ENTITY intagg          SYSTEM "intagg.sgml">
 <!ENTITY intarray        SYSTEM "intarray.sgml">
diff --git a/doc/src/sgml/gapless-seq.sgml b/doc/src/sgml/gapless-seq.sgml
new file mode 100644
index 0000000..44cc224
--- /dev/null
+++ b/doc/src/sgml/gapless-seq.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless-seq" xreflabel="gapless_seq">
+ <title>gapless_seq</title>
+
+ <indexterm zone="gapless-seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence and hold
+  lock on the sequence itself, so any concurrent nextval() calls have to wait
+  for the transaction to finish. This could be very costly and is not
+  recommended for general usage except in specific applications that require
+  this feature.
+ </para>
+
+</sect1>
-- 
1.9.1

0001-seqam-2016-03-31.patchtext/x-diff; name=0001-seqam-2016-03-31.patchDownload
From 366f7187b69eaba69514f46aa594ba09e84bbc0e Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Wed, 2 Mar 2016 16:15:33 +0100
Subject: [PATCH 1/2] seqam

---
 doc/src/sgml/filelist.sgml                    |    1 +
 doc/src/sgml/postgres.sgml                    |    1 +
 doc/src/sgml/ref/alter_sequence.sgml          |   55 +
 doc/src/sgml/ref/create_access_method.sgml    |    9 +-
 doc/src/sgml/ref/create_sequence.sgml         |   25 +
 doc/src/sgml/seqam.sgml                       |  312 +++++
 src/backend/access/Makefile                   |    4 +-
 src/backend/access/common/reloptions.c        |   13 +-
 src/backend/access/sequence/Makefile          |   17 +
 src/backend/access/sequence/seqamapi.c        |  191 ++++
 src/backend/access/sequence/seqlocal.c        |  414 +++++++
 src/backend/bootstrap/bootparse.y             |    1 +
 src/backend/catalog/heap.c                    |   17 +-
 src/backend/catalog/objectaddress.c           |    1 +
 src/backend/catalog/toasting.c                |    1 +
 src/backend/commands/amcmds.c                 |   38 +-
 src/backend/commands/cluster.c                |    1 +
 src/backend/commands/createas.c               |    3 +-
 src/backend/commands/indexcmds.c              |    2 +-
 src/backend/commands/sequence.c               | 1515 +++++++++++++++----------
 src/backend/commands/tablecmds.c              |   49 +-
 src/backend/commands/typecmds.c               |    3 +-
 src/backend/commands/view.c                   |    3 +-
 src/backend/nodes/copyfuncs.c                 |    4 +
 src/backend/nodes/equalfuncs.c                |    4 +
 src/backend/parser/gram.y                     |   91 +-
 src/backend/parser/parse_utilcmd.c            |    3 +
 src/backend/tcop/utility.c                    |    6 +-
 src/backend/utils/adt/pseudotypes.c           |   25 +
 src/backend/utils/cache/relcache.c            |   53 +-
 src/backend/utils/misc/guc.c                  |    1 +
 src/bin/pg_dump/pg_dump.c                     |  132 ++-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/psql/describe.c                       |  112 +-
 src/include/access/reloptions.h               |    2 +-
 src/include/access/seqamapi.h                 |  109 ++
 src/include/catalog/heap.h                    |    1 +
 src/include/catalog/pg_am.h                   |    5 +
 src/include/catalog/pg_proc.h                 |   19 +
 src/include/catalog/pg_type.h                 |    7 +
 src/include/commands/defrem.h                 |    1 +
 src/include/commands/sequence.h               |   30 +-
 src/include/commands/tablecmds.h              |    2 +-
 src/include/nodes/nodes.h                     |    1 +
 src/include/nodes/parsenodes.h                |    8 +-
 src/include/utils/builtins.h                  |    2 +
 src/include/utils/rel.h                       |    8 +-
 src/test/regress/expected/create_am.out       |   19 +
 src/test/regress/expected/create_view.out     |    5 +-
 src/test/regress/expected/opr_sanity.out      |    4 +-
 src/test/regress/expected/sequence.out        |   35 +-
 src/test/regress/expected/updatable_views.out |   95 +-
 src/test/regress/sql/create_am.sql            |   16 +
 src/test/regress/sql/create_view.sql          |    5 +-
 src/test/regress/sql/opr_sanity.sql           |    4 +-
 src/test/regress/sql/sequence.sql             |   10 +-
 56 files changed, 2712 insertions(+), 784 deletions(-)
 create mode 100644 doc/src/sgml/seqam.sgml
 create mode 100644 src/backend/access/sequence/Makefile
 create mode 100644 src/backend/access/sequence/seqamapi.c
 create mode 100644 src/backend/access/sequence/seqlocal.c
 create mode 100644 src/include/access/seqamapi.h

diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 30adece..fd355dc 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 7e82cdc..d37058a 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -251,6 +251,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
index 3c091f8..8676a84 100644
--- a/doc/src/sgml/ref/create_access_method.sgml
+++ b/doc/src/sgml/ref/create_access_method.sgml
@@ -61,7 +61,8 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
     <listitem>
      <para>
       This clause specifies type of access method to define.
-      Only <literal>INDEX</literal> is supported at present.
+      Supported values are <literal>INDEX</literal> for index access methods
+      and <literal>SEQUENCE</literal> for sequence access methods.
      </para>
     </listitem>
    </varlistentry>
@@ -75,7 +76,9 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
       of access method to the core.  The handler function must take single
       argument of type <type>internal</>, and its return type depends on the
       type of access method; for <literal>INDEX</literal> access methods, it
-      must be <type>index_am_handler</type>.
+      must be <type>index_am_handler</type> and for
+      <literal>SEQUENCE</literal> access methods it must be
+      <type>sequence_am_handler</type>.
      </para>
 
      <para>
@@ -114,6 +117,8 @@ CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;
    <member><xref linkend="sql-drop-access-method"></member>
    <member><xref linkend="sql-createopclass"></member>
    <member><xref linkend="sql-createopfamily"></member>
+   <member><xref linkend="index-api"> </member>
+   <member><xref linkend="seqam"></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..5faeae3
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,312 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_am</structname> system catalog (see
+   <xref linkend="catalog-pg-am">).
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The SQL interface of the sequence access method consists of a handler
+   function which has to return special pseudo-type
+   <type>sequence_handler</type>. This function must be written in a compiled
+   language such as C, using the version-1 interface. For details on C
+   language calling conventions and dynamic loading, see
+   <xref linkend="xfunc-c">.
+  </para>
+
+  <para>
+   The actual C structure returned by the handler function is following:
+<programlisting>
+typedef struct SeqAmRoutine
+{
+    NodeTag        type;
+
+    /* Custom datatype used for storing state of the sequence by the AM */
+    Oid            StateTypeOid;
+
+    /* Function for parsing reloptions */
+    ParseRelOptions_function    amoptions;
+
+    /* Initialization */
+    SeqAMInit_function          Init;
+
+    /* nextval handler */
+    SeqAMAlloc_function         Alloc;
+
+    /* State manipulation functions */
+    SeqAMSetval_function        Setval;
+    SeqAMGetState_function      GetState;
+    SeqAMSetState_function      SetState;
+} SeqAmRoutine;
+</programlisting>
+  </para>
+
+  <para>
+   These are the functions implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+Datum
+SeqAMInit (Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+           bool restart_requested, bool is_init)
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   The <literal>seq</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the Datum of a state of the sequence. The type
+   of the Datum is the <structname>StateTypeOid</structname> type.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+ParseRelOptions (Datum reloptions, bool validate)
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+SeqAMAlloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+            int64 *last)
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum SeqAMGetState (Relation seqrel, SequenceHandle *seqh)
+</programlisting>
+   Dump the current state of the sequence. The return value is the Datum of
+   a <structname>StateTypeOid</structname>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetState (Relation seqrel, SequenceHandle *seqh, Datum amstate)
+</programlisting>
+   Restore state of the sequence based on amstate which is Datum of a
+   <structname>StateTypeOid</structname> type.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following helper functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh)
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+Form_pg_sequence
+(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns the whole pg_sequence tuple as the
+   <structname>Form_pg_sequence</structname> structure.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum
+sequence_read_state(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns just the sequence access method specific state Datum.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_save_state(SequenceHandle *seqh, Datum seqstate, bool dowal)
+</programlisting>
+   Save the modified sequence state tuple indicating if the change should be
+   WAL logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh)
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_save_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh)
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_options</>
+   and/or added by <function>sequence_read_state</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_save_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors)
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname)
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a..223403d 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,7 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  tablesample transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc sequence \
+			  spgist tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ea0755a..bb368d2 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -888,8 +888,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
- * AM's options parser function in the case of a tuple corresponding to an
- * index, or NULL otherwise.
+ * or sequence AM's options parser function in the case of a tuple corresponding
+ * to an index or sequence respectively, or NULL otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
@@ -921,7 +921,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1374,14 +1375,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for access methods.
  *
- *	amoptions	index AM's option parser function
+ *	amoptions	AM's option parser function
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+am_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 {
 	Assert(amoptions != NULL);
 
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..1784799
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqamapi.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqamapi.c b/src/backend/access/sequence/seqamapi.c
new file mode 100644
index 0000000..ef8e7a4
--- /dev/null
+++ b/src/backend/access/sequence/seqamapi.c
@@ -0,0 +1,191 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqamapi.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amstate.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_am.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * GetSeqAmRoutine - call the specified access method handler routine
+ * to get its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutine(Oid amhandler)
+{
+	Datum			datum;
+	SeqAmRoutine   *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SeqAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SeqAmRoutine))
+		elog(ERROR, "sequence access method handler function %u did not return an SeqAmRoutine struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * GetSeqAmRoutineByAMId - look up the handler of the sequence access method
+ * for the given OID, and retrieve its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineByAMId(Oid amoid)
+{
+	HeapTuple		tuple;
+	regproc			amhandler;
+	Form_pg_am		amform;
+
+	/* 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);
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("sequence access method \"%s\" does not have a handler",
+						NameStr(amform->amname))));
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function. */
+	return GetSeqAmRoutine(amhandler);
+}
+
+/*
+ * GetSeqAmRoutineForRelation - look up the handler of the sequence access
+ * method for the given sequence Relation.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineForRelation(Relation relation)
+{
+	SeqAmRoutine *seqam;
+	SeqAmRoutine *cseqam;
+
+	if (relation->rd_seqamroutine == NULL)
+	{
+		if (!OidIsValid(relation->rd_rel->relam))
+		{
+			char *relname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(relation)),
+													   RelationGetRelationName(relation));
+			elog(ERROR, "relation %s isn't sequence", relname);
+		}
+
+		seqam = GetSeqAmRoutineByAMId(relation->rd_rel->relam);
+		/* Save the data for later reuse in CacheMemoryContext */
+		cseqam = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												   sizeof(SeqAmRoutine));
+		memcpy(cseqam, seqam, sizeof(SeqAmRoutine));
+		relation->rd_seqamroutine = cseqam;
+	}
+
+	return relation->rd_seqamroutine;
+}
+
+/*
+ * ValidateSeqAmRoutine - to sanity check for the SeqAmRoutine returned by a
+ * handler.
+ *
+ * At the moment we only check that the StateTypeOid is valid type oid and that
+ * the type it points to is fixed width and will fit into the sequence page.
+ */
+void
+ValidateSeqAmRoutine(SeqAmRoutine *routine)
+{
+#define MAX_SEQAM_STATE_LEN \
+	MaxHeapTupleSize - MinHeapTupleSize - MAXALIGN(sizeof(FormData_pg_sequence))
+
+	Oid	stattypeoid = routine->StateTypeOid;
+
+	if (!get_typisdefined(stattypeoid))
+		ereport(ERROR,
+				(errmsg("type with OID %u does not exist",
+						stattypeoid)));
+
+	if (TypeIsToastable(stattypeoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" must not be toastable",
+						format_type_be(stattypeoid))));
+
+	if (get_typlen(stattypeoid) >= MAX_SEQAM_STATE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" exceeds maximum allowed length (%lu)",
+						format_type_be(stattypeoid), MAX_SEQAM_STATE_LEN)));
+
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..03ac01f
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,414 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+#include "utils/typcache.h"
+
+typedef struct LocalSequence
+{
+	FormData_pg_sequence	seq;
+	LocalSequenceState		state;
+} LocalSequence;
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+static bytea *seqam_local_reloptions(Datum reloptions, bool validate);
+static Datum seqam_local_init(Relation seqrel, Form_pg_sequence seq,
+							  int64 restart_value, bool restart_requested,
+							  bool is_init);
+static int64 seqam_local_alloc(Relation seqrel, SequenceHandle *seqh,
+							   int64 nrequested, int64 *last);
+static void seqam_local_setval(Relation seqrel, SequenceHandle *seqh,
+							   int64 new_value);
+static Datum seqam_local_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+								  Datum amstate);
+
+static char *
+seqam_local_state_get_part(StringInfo buf, char *ptr, bool *found)
+{
+	*found = false;
+
+	resetStringInfo(buf);
+
+	while (*ptr != ',' && *ptr != ')')
+	{
+		char		ch = *ptr++;
+
+		if (ch == '\0')
+		{
+			*found = false;
+			return ptr;
+		}
+
+		appendStringInfoChar(buf, ch);
+
+		if (!*found)
+			*found = true;
+	}
+
+	return ptr;
+}
+
+Datum
+seqam_local_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr;
+	Datum		d;
+	bool		found;
+	StringInfoData		buf;
+	LocalSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	initStringInfo(&buf);
+
+	/* last_value is required */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (!found)
+		goto malformed;
+	d = DirectFunctionCall1(int8in, CStringGetDatum(buf.data));
+	state->last_value = DatumGetInt64(d);
+	if (*ptr == ',')
+		ptr++;
+
+	/* is_called is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(boolin, CStringGetDatum(buf.data));
+		state->is_called = DatumGetBool(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->is_called = true;
+
+	/* log_cnt is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(int4in, CStringGetDatum(buf.data));
+		state->log_cnt = DatumGetInt32(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->log_cnt = 0;
+
+	if (*ptr != ')' || *(++ptr) != '\0')
+		goto malformed;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_local_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_local_state_out(PG_FUNCTION_ARGS)
+{
+	LocalSequenceState *state = (LocalSequenceState *) PG_GETARG_POINTER(0);
+	StringInfoData		buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("INT64_FORMAT",%s,%d)",
+					 state->last_value, state->is_called ? "t" : "f",
+					 state->log_cnt);
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the local sequence access method.
+ */
+Datum
+seqam_local_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine *seqam = makeNode(SeqAmRoutine);
+
+	seqam->StateTypeOid = SEQAMLOCALSTATEOID;
+	seqam->amoptions = seqam_local_reloptions;
+	seqam->Init = seqam_local_init;
+	seqam->Alloc = seqam_local_alloc;
+	seqam->Setval = seqam_local_setval;
+	seqam->GetState = seqam_local_get_state;
+	seqam->SetState = seqam_local_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+static bytea *
+seqam_local_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+static Datum
+seqam_local_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	LocalSequenceState	   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(LocalSequenceState));
+	else
+		seqstate = &((LocalSequence *) seq)->state;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->log_cnt = 0;
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+static int64
+seqam_local_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+				  int64 *last)
+{
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = &((LocalSequence *) seq)->state;
+
+	next = result = localseq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = localseq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_start_update(seqh, logit);
+
+	if (logit)
+	{
+		localseq->last_value = next;
+		localseq->log_cnt = 0;
+		localseq->is_called = true;
+
+		sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	}
+
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_save_state(seqh, PointerGetDatum(localseq), false);
+
+	sequence_finish_update(seqh);
+
+	return result;
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+static void
+seqam_local_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq;
+
+	amstate = sequence_read_state(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	localseq->last_value = new_value;		/* last fetched number */
+	localseq->is_called = true;
+	localseq->log_cnt = 0;			/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+static Datum
+seqam_local_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq = palloc(sizeof(LocalSequenceState));
+
+	amstate = sequence_read_state(seqh);
+	memcpy(localseq, DatumGetPointer(amstate), sizeof(LocalSequenceState));
+	sequence_release_tuple(seqh);
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+static void
+seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+					  Datum amstate)
+{
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(localseq->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 41d2fd4..e684f60 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -251,6 +251,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e997b57..b7cd40a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -41,6 +41,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
@@ -88,6 +89,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -849,6 +851,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -922,6 +925,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1035,6 +1039,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1263,6 +1268,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1275,7 +1281,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the pg_am record.
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1330,6 +1337,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = AccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index cb3ba85..b40f0c7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index f40a005..40b5e91 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -291,6 +291,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 68ea5f3..1f2e0c4 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -15,6 +15,8 @@
 
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_am.h"
@@ -29,7 +31,7 @@
 #include "utils/syscache.h"
 
 
-static Oid	lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid	lookup_am_handler_func(List *handler_name, char amtype);
 static const char *get_am_type_string(char amtype);
 
 
@@ -72,7 +74,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	/*
 	 * Get the handler function oid, verifying the AM type while at it.
 	 */
-	amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+	amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
 
 	/*
 	 * Insert tuple into pg_am.
@@ -187,6 +189,16 @@ get_index_am_oid(const char *amname, bool missing_ok)
 }
 
 /*
+ * get_seq_am_oid - given an access method name, look up its OID
+ *		and verify it corresponds to an sequence AM.
+ */
+Oid
+get_seq_am_oid(const char *amname, bool missing_ok)
+{
+	return get_am_type_oid(amname, AMTYPE_SEQUENCE, missing_ok);
+}
+
+/*
  * get_am_oid - given an access method name, look up its OID.
  * 		The type is not checked.
  */
@@ -226,6 +238,8 @@ get_am_type_string(char amtype)
 	{
 		case AMTYPE_INDEX:
 			return "INDEX";
+		case AMTYPE_SEQUENCE:
+			return "SEQUENCE";
 		default:
 			/* shouldn't happen */
 			elog(ERROR, "invalid access method type '%c'", amtype);
@@ -240,9 +254,10 @@ get_am_type_string(char amtype)
  * This function either return valid function Oid or throw an error.
  */
 static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
 {
 	Oid			handlerOid;
+	Oid			handler_type;
 	static const Oid funcargtypes[1] = {INTERNALOID};
 
 	if (handler_name == NIL)
@@ -257,16 +272,21 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
 	switch (amtype)
 	{
 		case AMTYPE_INDEX:
-			if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
-				ereport(ERROR,
-						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-						 errmsg("function %s must return type \"%s\"",
-								NameListToString(handler_name),
-								"index_am_handler")));
+			handler_type = INDEX_AM_HANDLEROID;
+			break;
+		case AMTYPE_SEQUENCE:
+			handler_type = SEQ_AM_HANDLEROID;
 			break;
 		default:
 			elog(ERROR, "unrecognized access method type \"%c\"", amtype);
 	}
 
+	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;
 }
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5cb28cf..54a22da 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -680,6 +680,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cb7a145..ab7b7a3 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 13b04e6..92ae4db 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -536,7 +536,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..f7edcee 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,16 +14,22 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/tupmacs.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +42,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,24 +82,113 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	Oid			statetyp;
+	int16		statetyplen;
+	bool		statetypbyval;
+	char		statetypalign;
+	bool		inupdate;
+	HeapTupleData tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
  */
 static SeqTableData *last_used_seq = NULL;
 
+static HeapTuple build_seq_tuple(Relation rel, SeqAmRoutine *seqam,
+								 Form_pg_sequence new, int64 restart_value);
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static HeapTuple sequence_read_tuple(SequenceHandle *seqh);
 
+/*
+ * Build template column definition for a sequence relation.
+*/
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_not_null = true;
+	coldef->is_from_type = false;
+	/* Force plain storage. */
+	coldef->storage = 'p';
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(Oid amstateTypeOid)
+{
+	List	   *seqcols;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		switch (colid)
+		{
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+			case SEQ_COL_AMSTATE:
+				coldef->typeName = makeTypeNameFromOid(amstateTypeOid, -1);
+				coldef->colname = "amstate";
+				break;
+		}
+
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	return seqcols;
+}
 
 /*
  * DefineSequence
@@ -111,14 +201,12 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
+	List	   *seqcols;
+	SeqAmRoutine *seqam;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -147,102 +235,33 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
-	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
-
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
-		{
-			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
-				break;
-			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
-				break;
-			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
-				break;
-			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
-				break;
-			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
-				break;
-			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
-			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
-				break;
-		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
-	}
+	if (seq->accessMethod)
+		seqamid = get_seq_am_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
+	seqam = GetSeqAmRoutineByAMId(seqamid);
+	/* Build column definitions. */
+	seqcols = BuildSeqColumnDefList(seqam->StateTypeOid);
 
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
+	stmt->tableElts = seqcols;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	/* Build new sequence tuple and store it. */
+	tuple = build_seq_tuple(rel, seqam, &new, new.start_value);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +273,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +287,89 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
+	TupleDesc			tupDesc;
+	Datum	   values[SEQ_COL_LASTCOL];
+	bool	   nulls[SEQ_COL_LASTCOL];
+	SeqAmRoutine *seqam;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
-	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tupDesc = RelationGetDescr(seqrel);
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(seqrel, seq, seq->start_value,
+											  true, false);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
+}
+
+/*
+ * Build sequence tuple based on the sequence form and fill in the
+ * sequence AM specific info as well.
+ */
+static HeapTuple
+build_seq_tuple(Relation rel, SeqAmRoutine *seqam, Form_pg_sequence new,
+				int64 restart_value)
+{
+	TupleDesc	tupDesc;
+	HeapTuple	tuple;
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+
+	tupDesc = RelationGetDescr(rel);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new->start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new->increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new->max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new->min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new->cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new->is_cycled);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(rel, new, restart_value,
+											  false, true);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	return tuple;
 }
 
 /*
@@ -361,7 +412,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +432,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,6 +440,69 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 }
 
 /*
+ * Replace the type of amstate column.
+ *
+ * We don't do AlterTable here as that produces dead columns which we don't
+ * want. This is safe because the sequence page is controlled by code in this
+ * module and isn't changed the same way as a table.
+ *
+ * TODO: check if anybody is depending on the row-type associated with the
+ * sequence.
+ */
+static void
+replace_sequence_amstate_col(Oid seqrelid, Oid typid)
+{
+	Relation	attr_rel;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	HeapTuple	tp,
+				attr_tuple,
+				newattr_tuple;
+	Form_pg_type	typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+
+	memset(nulls, 0, sizeof(nulls));
+	memset(replace, 0, sizeof(replace));
+
+	replace[Anum_pg_attribute_atttypid - 1] = true;
+	replace[Anum_pg_attribute_attlen - 1] = true;
+	replace[Anum_pg_attribute_attbyval - 1] = true;
+	replace[Anum_pg_attribute_attalign - 1] = true;
+
+	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(typid);
+	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(typtup->typlen);
+	values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(typtup->typbyval);
+	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(typtup->typalign);
+
+	/* Build DROP command for amstate of old AM. */
+	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+	attr_tuple = SearchSysCache2(ATTNUM,
+								 ObjectIdGetDatum(seqrelid),
+								 Int16GetDatum(SEQ_COL_AMSTATE));
+	if (!HeapTupleIsValid(attr_tuple))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 SEQ_COL_AMSTATE, seqrelid);
+
+	newattr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel),
+									  values, nulls, replace);
+	simple_heap_update(attr_rel, &newattr_tuple->t_self, newattr_tuple);
+	CatalogUpdateIndexes(attr_rel, newattr_tuple);
+
+	ReleaseSysCache(tp);
+	heap_freetuple(newattr_tuple);
+	ReleaseSysCache(attr_tuple);
+
+	heap_close(attr_rel, RowExclusiveLock);
+}
+
+/*
  * AlterSequence
  *
  * Modify the definition of a sequence relation
@@ -406,19 +510,24 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence seq,
+					 new;
 	List	   *owned_by;
 	ObjectAddress address;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
+	SeqAmRoutine *oldseqam;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +535,187 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
+	oldamid = seqrel->rd_rel->relam;
+	oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	if (stmt->accessMethod)
+		seqamid = get_seq_am_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(stmt->options, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		HeapTuple       seqamtup;
+		Form_pg_am		form_am;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
+		SeqAmRoutine	   *newseqam;
+
+		oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = oldseqam->Alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
+
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(AMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
+
+		newseqam = GetSeqAmRoutineByAMId(seqamid);
+
+		form_am = (Form_pg_am) GETSTRUCT(seqamtup);
+
+		validnsps[0] = NameStr(form_am->amname);
+		validnsps[1] = NULL;
+
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		(void) am_reloptions(newseqam->amoptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
 
-		PageSetLSN(page, recptr);
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * And change the type definition.
+		 *
+		 * We can't use AlterTable internals here because the sequence
+		 * has to have the expected number of columns and no
+		 * attisdropped = true columns.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		replace_sequence_amstate_col(seqrelid, newseqam->StateTypeOid);
+		CommandCounterIncrement();
+
+		/* Rebuild the sequence tuple and save it. */
+		tuple = build_seq_tuple(seqrel, newseqam, new, restart_value);
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										AccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = AccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
+	else
+	{
+		Datum	   newamstate;
 
-	END_CRIT_SECTION();
+		sequence_check_range(restart_value, new->min_value, new->max_value,
+							 restart_requested ? "RESTART" : "START");
 
-	UnlockReleaseBuffer(buf);
+		/* Let the new sequence AM initialize. */
+		newamstate = oldseqam->Init(seqrel, new, restart_value,
+									restart_requested, false);
+
+		sequence_start_update(&seqh, true);
+		memcpy(seq, new, sizeof(FormData_pg_sequence));
+		sequence_save_state(&seqh, newamstate, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +756,26 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +800,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam->Alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +817,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for syncrep.
-	 * It's sufficient to ensure the toplevel transaction has an xid, no need
-	 * to assign xids subxacts, that'll already trigger an appropriate wait.
-	 * (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +828,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +889,26 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,106 +925,86 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
+	seqam->Setval(seqrel, &seqh, next);
 
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
-
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	last_used_seq = elm;
 
-	UnlockReleaseBuffer(buf);
+	sequence_close(&seqh);
 
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	LocalSequenceState	state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
-	do_setval(relid, next, true);
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
+
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Build the state and pass it to sequence AM. */
+	state.last_value = next;
+	state.log_cnt = 0;
+	state.is_called = iscalled;
+	seqam->SetState(seqh.rel, &seqh, PointerGetDatum(&state));
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1034,11 +1063,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1118,59 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->statetyp = InvalidOid;
+	seqh->statetyplen = -1;
+	seqh->statetypbyval = false;
+	seqh->statetypalign = '\0';
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(!seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1180,181 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+Form_pg_sequence
+sequence_read_options(SequenceHandle *seqh)
+{
+	return (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
+}
+
+Datum
+sequence_read_state(SequenceHandle *seqh)
+{
+	HeapTuple		tuple = sequence_read_tuple(seqh);
+	bool			isnull;
+	TupleDesc		tupDesc;
+	Datum			res;
+
+	tupDesc = RelationGetDescr(seqh->rel);
+
+	res = fastgetattr(tuple, SEQ_COL_AMSTATE, tupDesc, &isnull);
+	Assert(!isnull);
+
+	return res;
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool dowal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+	if (seqh->statetyp == InvalidOid)
+	{
+		SeqAmRoutine *seqam = GetSeqAmRoutineForRelation(seqh->rel);
+		seqh->statetyp = seqam->StateTypeOid;
+
+		get_typlenbyvalalign(seqh->statetyp, &seqh->statetyplen,
+							 &seqh->statetypbyval, &seqh->statetypalign);
+		Assert(seqh->statetyplen > 0);
+	}
+
+	if (dowal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_save_state(SequenceHandle *seqh, Datum amstate, bool dowal)
+{
+	char	   *seqstate = DatumGetPointer(sequence_read_state(seqh));
+	Page		page;
+
+	Assert(seqh->inupdate);
+
+
+	/*
+	 * Update the state data inline.
+	 *
+	 * This is only needed when the provided amstate datum points to different
+	 * data than what is already in the tuple.
+	 */
+	if (DatumGetPointer(amstate) != seqstate)
+	{
+		if (seqh->statetypbyval)
+			store_att_byval(seqstate, amstate, seqh->statetyplen);
+		else
+			memmove(seqstate, DatumGetPointer(amstate), seqh->statetyplen);
+	}
+
+	page = BufferGetPage(seqh->buf);
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (dowal && RelationNeedsWAL(seqh->rel))
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	Assert(BufferIsValid(seqh->buf));
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1364,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1441,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1449,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1458,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1466,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1473,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1486,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1539,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1553,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1663,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1691,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1699,85 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Datum		state;
+	char	   *statestr;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typoutput;
+	bool		typisvarlena;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type output function. */
+	getTypeOutputInfo(seqam->StateTypeOid, &typoutput, &typisvarlena);
+
+	/* Get the output and convert it to string. */
+	state = seqam->GetState(seqh.rel, &seqh);
+	statestr = OidOutputFunctionCall(typoutput, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_TEXT_P(cstring_to_text(statestr));
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *statestr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typinput,
+				typioparam;
+	Datum		state;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type input function. */
+	getTypeInputInfo(seqam->StateTypeOid, &typinput, &typioparam);
+
+	/* Convert the string to the state type and set it as new state. */
+	state = OidInputFunctionCall(typinput, statestr, typioparam, -1);
+	seqam->SetState(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1843,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 96dc923..6e6ee5d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -269,6 +270,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -454,7 +456,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -473,7 +475,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -551,13 +552,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		SeqAmRoutine *seqam;
+
+		Assert(relamid != InvalidOid);
+		seqam = GetSeqAmRoutineByAMId(relamid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 NULL, true, false);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		(void) am_reloptions(seqam->amoptions, reloptions, true);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -678,6 +695,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3308,7 +3326,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW |
+								ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4308,6 +4327,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4351,8 +4373,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a table, view, or foreign table");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9403,12 +9425,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqamroutine->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
+					 errmsg("\"%s\" is not a table, view, materialized view, index, sequence, or TOAST table",
 							RelationGetRelationName(rel))));
 			break;
 	}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 3de29b7..7aab5ab 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2116,7 +2116,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e9d9ba2..ca0f481 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f4e4a91..9ea0eb8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3565,7 +3565,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3578,7 +3580,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 854c062..5972ca6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1627,7 +1627,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1638,7 +1640,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1273352..75af985 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -310,7 +310,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <list>	event_trigger_when_list event_trigger_value_list
 %type <defelt>	event_trigger_when_item
-%type <chr>		enable_trigger
+%type <chr>		enable_trigger am_type
 
 %type <str>		copy_file_name
 				database_name access_method_clause access_method attr_name
@@ -605,7 +605,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 METHOD 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
@@ -3587,7 +3588,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3609,7 +3636,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3617,11 +3668,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3680,7 +3754,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
@@ -4715,16 +4789,21 @@ row_security_cmd:
  *
  *****************************************************************************/
 
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
 				{
 					CreateAmStmt *n = makeNode(CreateAmStmt);
 					n->amname = $4;
 					n->handler_name = $8;
-					n->amtype = AMTYPE_INDEX;
+					n->amtype = $6;
 					$$ = (Node *) n;
 				}
 		;
 
+am_type:
+			INDEX		{ $$ = AMTYPE_INDEX; }
+		|	SEQUENCE	{ $$ = AMTYPE_SEQUENCE; }
+		;
+
 /*****************************************************************************
  *
  *		QUERIES :
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6528494..509fa32 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -29,6 +29,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -456,6 +457,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4d0aac9..8b95577 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -965,7 +965,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -998,7 +999,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index dd447cf..6c1456e 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -399,6 +399,31 @@ index_am_handler_out(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();			/* keep compiler quiet */
 }
 
+/*
+ * seq_am_handler_int		- input routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_in(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * seq_am_handler_out		- output routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_out(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot display a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
 
 /*
  * tsm_handler_in		- input routine for pseudo-type TSM_HANDLER.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 130c06d..b9686da 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -260,6 +261,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -424,6 +426,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	amoptions_function	amoptions = NULL;
 
 	relation->rd_options = NULL;
 
@@ -432,10 +435,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_amroutine->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqamroutine->amoptions;
+			break;
 		default:
 			return;
 	}
@@ -447,8 +455,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_amroutine->amoptions : NULL);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1049,11 +1056,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1555,6 +1565,34 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation relation)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	SeqAmRoutine	   *result, *tmp;
+
+	/*
+	 * Look up the index's access method, save the OID of its handler function
+	 */
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 relation->rd_rel->relam);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amtype == AMTYPE_SEQUENCE);
+	relation->rd_amhandler = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	result = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												 sizeof(SeqAmRoutine));
+	tmp = GetSeqAmRoutine(relation->rd_amhandler);
+	memcpy(result, tmp, sizeof(SeqAmRoutine));
+	relation->rd_seqamroutine = result;
+}
 
 /*
  *		formrdesc
@@ -4885,6 +4923,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqamroutine == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 06cb166..fa4d39d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqamapi.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 454225d..6e6f5a0 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4747,6 +4747,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4798,6 +4799,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4840,6 +4842,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4882,6 +4885,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4924,6 +4928,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4964,6 +4969,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5003,6 +5009,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5042,6 +5049,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5081,6 +5089,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5119,6 +5128,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5153,6 +5163,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5182,6 +5193,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5221,6 +5233,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5274,6 +5287,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5339,6 +5353,10 @@ getTables(Archive *fout, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -11577,6 +11595,9 @@ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
 		case AMTYPE_INDEX:
 			appendPQExpBuffer(q, "TYPE INDEX ");
 			break;
+		case AMTYPE_SEQUENCE:
+			appendPQExpBuffer(q, "TYPE SEQUENCE ");
+			break;
 		default:
 			write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
 					  aminfo->amtype, qamname);
@@ -15317,7 +15338,8 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -15334,8 +15356,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	if (fout->remoteVersion >= 80400)
 	{
 		appendPQExpBuffer(query,
-						  "SELECT sequence_name, "
-						  "start_value, increment_by, "
+						  "SELECT start_value, increment_by, "
 				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
 				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
 						  "     ELSE max_value "
@@ -15351,8 +15372,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	else
 	{
 		appendPQExpBuffer(query,
-						  "SELECT sequence_name, "
-						  "0 AS start_value, increment_by, "
+						  "SELECT 0 AS start_value, increment_by, "
 				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
 				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
 						  "     ELSE max_value "
@@ -15377,24 +15397,40 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 		exit_nicely(1);
 	}
 
-	/* Disable this check: it fails if sequence has been renamed */
-#ifdef NOT_USED
-	if (strcmp(PQgetvalue(res, 0, 0), tbinfo->dobj.name) != 0)
+	startv = PQgetvalue(res, 0, 0);
+	incby = PQgetvalue(res, 0, 1);
+	if (!PQgetisnull(res, 0, 2))
+		maxv = PQgetvalue(res, 0, 2);
+	if (!PQgetisnull(res, 0, 3))
+		minv = PQgetvalue(res, 0, 3);
+	cache = PQgetvalue(res, 0, 4);
+	cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0);
+
+	/* 9.6 adds sequence access methods */
+	if (fout->remoteVersion >= 90600)
 	{
-		write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
-				  tbinfo->dobj.name, PQgetvalue(res, 0, 0));
-		exit_nicely(1);
-	}
-#endif
+		PGresult   *res2;
 
-	startv = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
-	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
-	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
-	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
+		printfPQExpBuffer(query, "SELECT a.amname\n"
+								 "FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
 
 	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
@@ -15437,6 +15473,13 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -15503,6 +15546,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15519,16 +15565,23 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/* On 9.6 there is special interface for dumping sequences */
+	if (fout->remoteVersion >= 90600)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15541,14 +15594,27 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90600)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index c02c536..c62abcc 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -231,6 +231,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_class) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 7b2f4e6..ac85da3 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1403,30 +1403,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1470,13 +1446,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1805,6 +1823,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1814,12 +1834,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.amname\n"
+								"FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 469ac67..69135aa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -273,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
+extern bytea *am_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqamapi.h b/src/include/access/seqamapi.h
new file mode 100644
index 0000000..a4c826a
--- /dev/null
+++ b/src/include/access/seqamapi.h
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqamapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAMAPI_H
+#define SEQAMAPI_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef bytea * (*ParseRelOptions_function) (Datum reloptions,
+											 bool validate);
+
+typedef Datum (*SeqAMInit_function) (Relation seqrel,
+									 Form_pg_sequence seq,
+									 int64 restart_value,
+									 bool restart_requested,
+									 bool is_init);
+
+typedef int64 (*SeqAMAlloc_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 nrequested,
+									  int64 *last);
+
+typedef void (*SeqAMSetval_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 new_value);
+
+typedef Datum (*SeqAMGetState_function) (Relation seqrel,
+										 SequenceHandle *seqh);
+typedef void (*SeqAMSetState_function) (Relation seqrel, SequenceHandle *seqh,
+										Datum amstate);
+
+typedef struct SeqAmRoutine
+{
+	NodeTag		type;
+
+	/* Custom columns needed by the AM */
+	Oid			StateTypeOid;
+
+	/* Function for parsing reloptions */
+	ParseRelOptions_function	amoptions;
+
+	/* Initialization */
+	SeqAMInit_function			Init;
+
+	/* nextval handler */
+	SeqAMAlloc_function			Alloc;
+
+	/* State manipulation functions */
+	SeqAMSetval_function		Setval;
+	SeqAMGetState_function		GetState;
+	SeqAMSetState_function		SetState;
+} SeqAmRoutine;
+
+extern SeqAmRoutine *GetSeqAmRoutine(Oid seqamhandler);
+extern SeqAmRoutine *GetSeqAmRoutineByAMId(Oid amoid);
+extern SeqAmRoutine *GetSeqAmRoutineForRelation(Relation relation);
+extern void ValidateSeqAmRoutine(SeqAmRoutine *routine);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern Form_pg_sequence sequence_read_options(SequenceHandle *seqh);
+extern Datum sequence_read_state(SequenceHandle *seqh);
+extern void sequence_start_update(SequenceHandle *seqh, bool dowal);
+extern void sequence_save_state(SequenceHandle *seqh, Datum seqstate,
+								bool dowal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+/* We need this public for serval3 */
+typedef struct LocalSequenceState
+{
+	int64   last_value;
+	int32	log_cnt;
+	bool	is_called;
+} LocalSequenceState;
+
+extern Datum seqam_local_state_in(PG_FUNCTION_ARGS);
+extern Datum seqam_local_state_out(PG_FUNCTION_ARGS);
+extern Datum seqam_local_handler(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAMAPI_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..d6b3cd7 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 1116923..9c615f7 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
  * ----------------
  */
 #define AMTYPE_INDEX					'i'		/* index access method */
+#define	AMTYPE_SEQUENCE					'S'		/* sequence access method */
 
 /* ----------------
  *		initial contents of pg_am
@@ -84,4 +85,8 @@ DATA(insert OID = 3580 (  brin		brinhandler i ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
+DATA(insert OID = 6023 (  local		seqam_local_handler	S ));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6023
+
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c86b920..54a712c 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1759,6 +1759,10 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v u 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 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 25 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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 s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3690,6 +3694,10 @@ DATA(insert OID = 326  (  index_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f
 DESCR("I/O");
 DATA(insert OID = 327  (  index_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
 DESCR("I/O");
+DATA(insert OID = 6021  (  seq_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 6020 "2275" _null_ _null_ _null_ _null_ _null_ seq_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6022 (  seq_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6020" _null_ _null_ _null_ _null_ _null_ seq_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
 DATA(insert OID = 3311 (  tsm_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 3312 (  tsm_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
@@ -3701,6 +3709,10 @@ DESCR("BERNOULLI tablesample method handler");
 DATA(insert OID = 3314 (  system			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_system_handler _null_ _null_ _null_ ));
 DESCR("SYSTEM tablesample method handler");
 
+/* sequence access method handlers */
+DATA(insert OID = 6024 (  seqam_local_handler	   PGNSP PGUID 12 1 0 0 0 f f f f f f v s 1 0 6020 "2281" _null_ _null_ _null_ _null_ _null_ seqam_local_handler _null_ _null_ _null_ ));
+DESCR("Local SequenceAM handler");
+
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
 DESCR("MD5 hash");
@@ -5270,6 +5282,13 @@ DESCR("pg_controldata recovery state information as a function");
 DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
 DESCR("pg_controldata init state information as a function");
 
+/* Sequence AM */
+DATA(insert OID = 6027 (  seqam_local_state_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 6025 "2275" _null_ _null_ _null_ _null_ _null_ seqam_local_state_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6028 (  seqam_local_state_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6025" _null_ _null_ _null_ _null_ _null_ seqam_local_state_out _null_ _null_ _null_ ));
+DESCR("I/O");
+
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2c90b76..7f020fe 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -696,11 +696,18 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 #define FDW_HANDLEROID	3115
 DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define INDEX_AM_HANDLEROID	325
+DATA(insert OID = 6020 ( seq_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 seq_am_handler_in seq_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQ_AM_HANDLEROID	6020
 DATA(insert OID = 3310 ( tsm_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define TSM_HANDLEROID	3310
 DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 #define ANYRANGEOID		3831
 
+/* Sequence AM composite types */
+DATA(insert OID = 6025 ( seqam_local_state	PGNSP PGUID 16 f b U f t \054 0 0 6026 seqam_local_state_in seqam_local_state_out - - - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQAMLOCALSTATEOID 6025
+DATA(insert OID = 6026 ( _seqam_local_state	PGNSP PGUID -1 f b A f t \054 0 6025 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 
 /*
  * macros
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index b064eb4..c9bf431 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -139,6 +139,7 @@ extern Datum transformGenericOptions(Oid catalogId,
 extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
 extern void RemoveAccessMethodById(Oid amOid);
 extern Oid	get_index_am_oid(const char *amname, bool missing_ok);
+extern Oid	get_seq_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);
 
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..2ff89b4 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -19,20 +19,17 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "storage/relfilenode.h"
-
+#include "access/htup_details.h"
 
 typedef struct FormData_pg_sequence
 {
-	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
+	/* amstate follows */
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,19 +38,16 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  * Columns of a sequence relation
  */
 
-#define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_STARTVAL		1
+#define SEQ_COL_INCBY			2
+#define SEQ_COL_MAXVALUE		3
+#define SEQ_COL_MINVALUE		4
+#define SEQ_COL_CACHE			5
+#define SEQ_COL_CYCLE			6
+#define SEQ_COL_AMSTATE			7
 
-#define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_FIRSTCOL		SEQ_COL_STARTVAL
+#define SEQ_COL_LASTCOL			SEQ_COL_AMSTATE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +66,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 7a770f4..1bceec8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 734df77..752d3a1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -477,6 +477,7 @@ typedef enum NodeTag
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
+	T_SeqAmRoutine,				/* in access/seqamapi.h */
 	T_TsmRoutine				/* in access/tsmapi.h */
 } NodeTag;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8b958b4..859fbad 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2205,8 +2205,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2214,8 +2216,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index d5c4b01..cf7e01b 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -587,6 +587,8 @@ extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
 extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_in(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_out(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
 extern Datum internal_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f2bebf2..4fe7e52 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -106,13 +106,18 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	Oid			rd_amhandler;	/* OID of access method's handler function */
+
+	/* These are non-NULL only for a sequence relation */
+	struct SeqAmRoutine *rd_seqamroutine;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
@@ -126,7 +131,6 @@ typedef struct RelationData
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
-	Oid			rd_amhandler;	/* OID of index AM's handler function */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
 	/* use "struct" here to avoid needing to include amapi.h: */
 	struct IndexAmRoutine *rd_amroutine;		/* index AM's API struct */
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index 47d6024..9242982 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -106,3 +106,22 @@ drop cascades to index grect2ind
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+-- Checks for sequence access methods
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+ERROR:  cannot drop access method local2 because other objects depend on it
+DETAIL:  sequence test_seqam depends on access method local2
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ae50c94..f1b1fc2 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -129,8 +129,9 @@ NOTICE:  view "v12_temp" will be a temporary view
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 NOTICE:  view "v13_temp" will be a temporary view
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index a8dc0c1..1c51a5e 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1586,7 +1586,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
  oid | amname | oid | proname 
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3a19cde 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate 
+-------------+--------------+---------------------+-----------+-------------+-----------+---------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (1,f,0)
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate  
+-------------+--------------+---------------------+-----------+-------------+-----------+----------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (2,t,31)
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c5dfbb5..a30582e 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,52 @@ SELECT table_name, column_name, is_updatable
   FROM information_schema.columns
  WHERE table_name LIKE E'r_\\_view%'
  ORDER BY table_name, ordinal_position;
- table_name |  column_name  | is_updatable 
-------------+---------------+--------------
- ro_view1   | a             | NO
- ro_view1   | b             | NO
- ro_view10  | a             | NO
- ro_view11  | a             | NO
- ro_view11  | b             | NO
- ro_view12  | a             | NO
- ro_view13  | a             | NO
- ro_view13  | b             | NO
- ro_view17  | a             | NO
- ro_view17  | b             | NO
- ro_view18  | a             | NO
- ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
- ro_view19  | start_value   | NO
- ro_view19  | increment_by  | NO
- ro_view19  | max_value     | NO
- ro_view19  | min_value     | NO
- ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
- ro_view19  | is_cycled     | NO
- ro_view19  | is_called     | NO
- ro_view2   | a             | NO
- ro_view2   | b             | NO
- ro_view20  | a             | NO
- ro_view20  | b             | NO
- ro_view20  | g             | NO
- ro_view3   | ?column?      | NO
- ro_view4   | count         | NO
- ro_view5   | a             | NO
- ro_view5   | rank          | NO
- ro_view6   | a             | NO
- ro_view6   | b             | NO
- ro_view7   | a             | NO
- ro_view7   | b             | NO
- ro_view8   | a             | NO
- ro_view8   | b             | NO
- ro_view9   | a             | NO
- ro_view9   | b             | NO
- rw_view14  | ctid          | NO
- rw_view14  | a             | YES
- rw_view14  | b             | YES
- rw_view15  | a             | YES
- rw_view15  | upper         | NO
- rw_view16  | a             | YES
- rw_view16  | b             | YES
- rw_view16  | aa            | YES
-(46 rows)
+ table_name | column_name  | is_updatable 
+------------+--------------+--------------
+ ro_view1   | a            | NO
+ ro_view1   | b            | NO
+ ro_view10  | a            | NO
+ ro_view11  | a            | NO
+ ro_view11  | b            | NO
+ ro_view12  | a            | NO
+ ro_view13  | a            | NO
+ ro_view13  | b            | NO
+ ro_view17  | a            | NO
+ ro_view17  | b            | NO
+ ro_view18  | a            | NO
+ ro_view19  | start_value  | NO
+ ro_view19  | increment_by | NO
+ ro_view19  | max_value    | NO
+ ro_view19  | min_value    | NO
+ ro_view19  | cache_value  | NO
+ ro_view19  | is_cycled    | NO
+ ro_view19  | amstate      | NO
+ ro_view2   | a            | NO
+ ro_view2   | b            | NO
+ ro_view20  | a            | NO
+ ro_view20  | b            | NO
+ ro_view20  | g            | NO
+ ro_view3   | ?column?     | NO
+ ro_view4   | count        | NO
+ ro_view5   | a            | NO
+ ro_view5   | rank         | NO
+ ro_view6   | a            | NO
+ ro_view6   | b            | NO
+ ro_view7   | a            | NO
+ ro_view7   | b            | NO
+ ro_view8   | a            | NO
+ ro_view8   | b            | NO
+ ro_view9   | a            | NO
+ ro_view9   | b            | NO
+ rw_view14  | ctid         | NO
+ rw_view14  | a            | YES
+ rw_view14  | b            | YES
+ rw_view15  | a            | YES
+ rw_view15  | upper        | NO
+ rw_view16  | a            | YES
+ rw_view16  | b            | YES
+ rw_view16  | aa           | YES
+(43 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index e2051c5..e811ea3 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -71,3 +71,19 @@ DROP ACCESS METHOD gist2 CASCADE;
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+
+-- Checks for sequence access methods
+
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 58d361d..e967fff 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -131,8 +131,9 @@ CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 6c9784a..6c44d70 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1055,7 +1055,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
 
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..c2b1164 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
-- 
1.9.1

#122Fabrízio de Royes Mello
fabriziomello@gmail.com
In reply to: Petr Jelinek (#121)
2 attachment(s)
Re: Sequence Access Method WIP

On Thu, Mar 31, 2016 at 9:19 AM, Petr Jelinek <petr@2ndquadrant.com> wrote:

Hi,

new version attached that should fix the issue. It was alignment -

honestly don't know what I was thinking using fixed alignment when the AMs
can define their own type.

Yeah... now all works fine... I had a minor issue when apply to the current
master but the attached fix it and I also added tab-complete support for
CREATE SEQUENCE...USING and ALTER SEQUENCE...USING.

I ran all the regression tests and all passed.

I have just one question about de 0002 patch:
- There are some reason to not use TransactionId instead of uint32 in
GaplessSequenceState struct?

Regards,

--
Fabrízio de Royes Mello
Consultoria/Coaching PostgreSQL

Show quoted text

Timbira: http://www.timbira.com.br
Blog: http://fabriziomello.github.io
Linkedin: http://br.linkedin.com/in/fabriziomello
Twitter: http://twitter.com/fabriziomello
Github: http://github.com/fabriziomello

Attachments:

0001-seqam-2016-04-04.patchtext/x-diff; charset=US-ASCII; name=0001-seqam-2016-04-04.patchDownload
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 6c0ad3f..e2bf725 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 0346d36..6b9bae8 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -252,6 +252,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
index 3c091f8..8676a84 100644
--- a/doc/src/sgml/ref/create_access_method.sgml
+++ b/doc/src/sgml/ref/create_access_method.sgml
@@ -61,7 +61,8 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
     <listitem>
      <para>
       This clause specifies type of access method to define.
-      Only <literal>INDEX</literal> is supported at present.
+      Supported values are <literal>INDEX</literal> for index access methods
+      and <literal>SEQUENCE</literal> for sequence access methods.
      </para>
     </listitem>
    </varlistentry>
@@ -75,7 +76,9 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
       of access method to the core.  The handler function must take single
       argument of type <type>internal</>, and its return type depends on the
       type of access method; for <literal>INDEX</literal> access methods, it
-      must be <type>index_am_handler</type>.
+      must be <type>index_am_handler</type> and for
+      <literal>SEQUENCE</literal> access methods it must be
+      <type>sequence_am_handler</type>.
      </para>
 
      <para>
@@ -114,6 +117,8 @@ CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;
    <member><xref linkend="sql-drop-access-method"></member>
    <member><xref linkend="sql-createopclass"></member>
    <member><xref linkend="sql-createopfamily"></member>
+   <member><xref linkend="index-api"> </member>
+   <member><xref linkend="seqam"></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..5faeae3
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,312 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_am</structname> system catalog (see
+   <xref linkend="catalog-pg-am">).
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The SQL interface of the sequence access method consists of a handler
+   function which has to return special pseudo-type
+   <type>sequence_handler</type>. This function must be written in a compiled
+   language such as C, using the version-1 interface. For details on C
+   language calling conventions and dynamic loading, see
+   <xref linkend="xfunc-c">.
+  </para>
+
+  <para>
+   The actual C structure returned by the handler function is following:
+<programlisting>
+typedef struct SeqAmRoutine
+{
+    NodeTag        type;
+
+    /* Custom datatype used for storing state of the sequence by the AM */
+    Oid            StateTypeOid;
+
+    /* Function for parsing reloptions */
+    ParseRelOptions_function    amoptions;
+
+    /* Initialization */
+    SeqAMInit_function          Init;
+
+    /* nextval handler */
+    SeqAMAlloc_function         Alloc;
+
+    /* State manipulation functions */
+    SeqAMSetval_function        Setval;
+    SeqAMGetState_function      GetState;
+    SeqAMSetState_function      SetState;
+} SeqAmRoutine;
+</programlisting>
+  </para>
+
+  <para>
+   These are the functions implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+Datum
+SeqAMInit (Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+           bool restart_requested, bool is_init)
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   The <literal>seq</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the Datum of a state of the sequence. The type
+   of the Datum is the <structname>StateTypeOid</structname> type.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+ParseRelOptions (Datum reloptions, bool validate)
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+SeqAMAlloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+            int64 *last)
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum SeqAMGetState (Relation seqrel, SequenceHandle *seqh)
+</programlisting>
+   Dump the current state of the sequence. The return value is the Datum of
+   a <structname>StateTypeOid</structname>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetState (Relation seqrel, SequenceHandle *seqh, Datum amstate)
+</programlisting>
+   Restore state of the sequence based on amstate which is Datum of a
+   <structname>StateTypeOid</structname> type.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following helper functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh)
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+Form_pg_sequence
+(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns the whole pg_sequence tuple as the
+   <structname>Form_pg_sequence</structname> structure.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum
+sequence_read_state(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns just the sequence access method specific state Datum.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_save_state(SequenceHandle *seqh, Datum seqstate, bool dowal)
+</programlisting>
+   Save the modified sequence state tuple indicating if the change should be
+   WAL logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh)
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_save_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh)
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_options</>
+   and/or added by <function>sequence_read_state</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_save_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors)
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname)
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a..223403d 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,7 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  tablesample transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc sequence \
+			  spgist tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ea0755a..bb368d2 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -888,8 +888,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
- * AM's options parser function in the case of a tuple corresponding to an
- * index, or NULL otherwise.
+ * or sequence AM's options parser function in the case of a tuple corresponding
+ * to an index or sequence respectively, or NULL otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
@@ -921,7 +921,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1374,14 +1375,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for access methods.
  *
- *	amoptions	index AM's option parser function
+ *	amoptions	AM's option parser function
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+am_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 {
 	Assert(amoptions != NULL);
 
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..1784799
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqamapi.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqamapi.c b/src/backend/access/sequence/seqamapi.c
new file mode 100644
index 0000000..ef8e7a4
--- /dev/null
+++ b/src/backend/access/sequence/seqamapi.c
@@ -0,0 +1,191 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqamapi.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amstate.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_am.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * GetSeqAmRoutine - call the specified access method handler routine
+ * to get its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutine(Oid amhandler)
+{
+	Datum			datum;
+	SeqAmRoutine   *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SeqAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SeqAmRoutine))
+		elog(ERROR, "sequence access method handler function %u did not return an SeqAmRoutine struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * GetSeqAmRoutineByAMId - look up the handler of the sequence access method
+ * for the given OID, and retrieve its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineByAMId(Oid amoid)
+{
+	HeapTuple		tuple;
+	regproc			amhandler;
+	Form_pg_am		amform;
+
+	/* 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);
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("sequence access method \"%s\" does not have a handler",
+						NameStr(amform->amname))));
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function. */
+	return GetSeqAmRoutine(amhandler);
+}
+
+/*
+ * GetSeqAmRoutineForRelation - look up the handler of the sequence access
+ * method for the given sequence Relation.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineForRelation(Relation relation)
+{
+	SeqAmRoutine *seqam;
+	SeqAmRoutine *cseqam;
+
+	if (relation->rd_seqamroutine == NULL)
+	{
+		if (!OidIsValid(relation->rd_rel->relam))
+		{
+			char *relname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(relation)),
+													   RelationGetRelationName(relation));
+			elog(ERROR, "relation %s isn't sequence", relname);
+		}
+
+		seqam = GetSeqAmRoutineByAMId(relation->rd_rel->relam);
+		/* Save the data for later reuse in CacheMemoryContext */
+		cseqam = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												   sizeof(SeqAmRoutine));
+		memcpy(cseqam, seqam, sizeof(SeqAmRoutine));
+		relation->rd_seqamroutine = cseqam;
+	}
+
+	return relation->rd_seqamroutine;
+}
+
+/*
+ * ValidateSeqAmRoutine - to sanity check for the SeqAmRoutine returned by a
+ * handler.
+ *
+ * At the moment we only check that the StateTypeOid is valid type oid and that
+ * the type it points to is fixed width and will fit into the sequence page.
+ */
+void
+ValidateSeqAmRoutine(SeqAmRoutine *routine)
+{
+#define MAX_SEQAM_STATE_LEN \
+	MaxHeapTupleSize - MinHeapTupleSize - MAXALIGN(sizeof(FormData_pg_sequence))
+
+	Oid	stattypeoid = routine->StateTypeOid;
+
+	if (!get_typisdefined(stattypeoid))
+		ereport(ERROR,
+				(errmsg("type with OID %u does not exist",
+						stattypeoid)));
+
+	if (TypeIsToastable(stattypeoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" must not be toastable",
+						format_type_be(stattypeoid))));
+
+	if (get_typlen(stattypeoid) >= MAX_SEQAM_STATE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" exceeds maximum allowed length (%lu)",
+						format_type_be(stattypeoid), MAX_SEQAM_STATE_LEN)));
+
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..03ac01f
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,414 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+#include "utils/typcache.h"
+
+typedef struct LocalSequence
+{
+	FormData_pg_sequence	seq;
+	LocalSequenceState		state;
+} LocalSequence;
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+static bytea *seqam_local_reloptions(Datum reloptions, bool validate);
+static Datum seqam_local_init(Relation seqrel, Form_pg_sequence seq,
+							  int64 restart_value, bool restart_requested,
+							  bool is_init);
+static int64 seqam_local_alloc(Relation seqrel, SequenceHandle *seqh,
+							   int64 nrequested, int64 *last);
+static void seqam_local_setval(Relation seqrel, SequenceHandle *seqh,
+							   int64 new_value);
+static Datum seqam_local_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+								  Datum amstate);
+
+static char *
+seqam_local_state_get_part(StringInfo buf, char *ptr, bool *found)
+{
+	*found = false;
+
+	resetStringInfo(buf);
+
+	while (*ptr != ',' && *ptr != ')')
+	{
+		char		ch = *ptr++;
+
+		if (ch == '\0')
+		{
+			*found = false;
+			return ptr;
+		}
+
+		appendStringInfoChar(buf, ch);
+
+		if (!*found)
+			*found = true;
+	}
+
+	return ptr;
+}
+
+Datum
+seqam_local_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr;
+	Datum		d;
+	bool		found;
+	StringInfoData		buf;
+	LocalSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	initStringInfo(&buf);
+
+	/* last_value is required */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (!found)
+		goto malformed;
+	d = DirectFunctionCall1(int8in, CStringGetDatum(buf.data));
+	state->last_value = DatumGetInt64(d);
+	if (*ptr == ',')
+		ptr++;
+
+	/* is_called is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(boolin, CStringGetDatum(buf.data));
+		state->is_called = DatumGetBool(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->is_called = true;
+
+	/* log_cnt is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(int4in, CStringGetDatum(buf.data));
+		state->log_cnt = DatumGetInt32(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->log_cnt = 0;
+
+	if (*ptr != ')' || *(++ptr) != '\0')
+		goto malformed;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_local_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_local_state_out(PG_FUNCTION_ARGS)
+{
+	LocalSequenceState *state = (LocalSequenceState *) PG_GETARG_POINTER(0);
+	StringInfoData		buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("INT64_FORMAT",%s,%d)",
+					 state->last_value, state->is_called ? "t" : "f",
+					 state->log_cnt);
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the local sequence access method.
+ */
+Datum
+seqam_local_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine *seqam = makeNode(SeqAmRoutine);
+
+	seqam->StateTypeOid = SEQAMLOCALSTATEOID;
+	seqam->amoptions = seqam_local_reloptions;
+	seqam->Init = seqam_local_init;
+	seqam->Alloc = seqam_local_alloc;
+	seqam->Setval = seqam_local_setval;
+	seqam->GetState = seqam_local_get_state;
+	seqam->SetState = seqam_local_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+static bytea *
+seqam_local_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+static Datum
+seqam_local_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	LocalSequenceState	   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(LocalSequenceState));
+	else
+		seqstate = &((LocalSequence *) seq)->state;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->log_cnt = 0;
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+static int64
+seqam_local_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+				  int64 *last)
+{
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = &((LocalSequence *) seq)->state;
+
+	next = result = localseq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = localseq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_start_update(seqh, logit);
+
+	if (logit)
+	{
+		localseq->last_value = next;
+		localseq->log_cnt = 0;
+		localseq->is_called = true;
+
+		sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	}
+
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_save_state(seqh, PointerGetDatum(localseq), false);
+
+	sequence_finish_update(seqh);
+
+	return result;
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+static void
+seqam_local_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq;
+
+	amstate = sequence_read_state(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	localseq->last_value = new_value;		/* last fetched number */
+	localseq->is_called = true;
+	localseq->log_cnt = 0;			/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+static Datum
+seqam_local_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq = palloc(sizeof(LocalSequenceState));
+
+	amstate = sequence_read_state(seqh);
+	memcpy(localseq, DatumGetPointer(amstate), sizeof(LocalSequenceState));
+	sequence_release_tuple(seqh);
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+static void
+seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+					  Datum amstate)
+{
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(localseq->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 41d2fd4..e684f60 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -251,6 +251,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e997b57..b7cd40a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -41,6 +41,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
@@ -88,6 +89,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -849,6 +851,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -922,6 +925,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1035,6 +1039,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1263,6 +1268,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1275,7 +1281,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the pg_am record.
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1330,6 +1337,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = AccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index cb3ba85..b40f0c7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index f40a005..40b5e91 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -291,6 +291,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 904dc1c..1f2e0c4 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -15,6 +15,8 @@
 
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_am.h"
@@ -29,7 +31,7 @@
 #include "utils/syscache.h"
 
 
-static Oid	lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid	lookup_am_handler_func(List *handler_name, char amtype);
 static const char *get_am_type_string(char amtype);
 
 
@@ -72,7 +74,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	/*
 	 * Get the handler function oid, verifying the AM type while at it.
 	 */
-	amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+	amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
 
 	/*
 	 * Insert tuple into pg_am.
@@ -187,6 +189,16 @@ get_index_am_oid(const char *amname, bool missing_ok)
 }
 
 /*
+ * get_seq_am_oid - given an access method name, look up its OID
+ *		and verify it corresponds to an sequence AM.
+ */
+Oid
+get_seq_am_oid(const char *amname, bool missing_ok)
+{
+	return get_am_type_oid(amname, AMTYPE_SEQUENCE, missing_ok);
+}
+
+/*
  * get_am_oid - given an access method name, look up its OID.
  * 		The type is not checked.
  */
@@ -226,6 +238,8 @@ get_am_type_string(char amtype)
 	{
 		case AMTYPE_INDEX:
 			return "INDEX";
+		case AMTYPE_SEQUENCE:
+			return "SEQUENCE";
 		default:
 			/* shouldn't happen */
 			elog(ERROR, "invalid access method type '%c'", amtype);
@@ -240,9 +254,10 @@ get_am_type_string(char amtype)
  * This function either return valid function Oid or throw an error.
  */
 static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
 {
 	Oid			handlerOid;
+	Oid			handler_type;
 	static const Oid funcargtypes[1] = {INTERNALOID};
 
 	if (handler_name == NIL)
@@ -257,16 +272,21 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
 	switch (amtype)
 	{
 		case AMTYPE_INDEX:
-			if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
-				ereport(ERROR,
-						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-						 errmsg("function %s must return type %s",
-								NameListToString(handler_name),
-								"index_am_handler")));
+			handler_type = INDEX_AM_HANDLEROID;
+			break;
+		case AMTYPE_SEQUENCE:
+			handler_type = SEQ_AM_HANDLEROID;
 			break;
 		default:
 			elog(ERROR, "unrecognized access method type \"%c\"", amtype);
 	}
 
+	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;
 }
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5cb28cf..54a22da 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -680,6 +680,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cb7a145..ab7b7a3 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 13b04e6..92ae4db 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -536,7 +536,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..f7edcee 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,16 +14,22 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/tupmacs.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +42,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,24 +82,113 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	Oid			statetyp;
+	int16		statetyplen;
+	bool		statetypbyval;
+	char		statetypalign;
+	bool		inupdate;
+	HeapTupleData tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
  */
 static SeqTableData *last_used_seq = NULL;
 
+static HeapTuple build_seq_tuple(Relation rel, SeqAmRoutine *seqam,
+								 Form_pg_sequence new, int64 restart_value);
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static HeapTuple sequence_read_tuple(SequenceHandle *seqh);
 
+/*
+ * Build template column definition for a sequence relation.
+*/
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_not_null = true;
+	coldef->is_from_type = false;
+	/* Force plain storage. */
+	coldef->storage = 'p';
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(Oid amstateTypeOid)
+{
+	List	   *seqcols;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		switch (colid)
+		{
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+			case SEQ_COL_AMSTATE:
+				coldef->typeName = makeTypeNameFromOid(amstateTypeOid, -1);
+				coldef->colname = "amstate";
+				break;
+		}
+
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	return seqcols;
+}
 
 /*
  * DefineSequence
@@ -111,14 +201,12 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
+	List	   *seqcols;
+	SeqAmRoutine *seqam;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -147,102 +235,33 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
-	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
-
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
-		{
-			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
-				break;
-			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
-				break;
-			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
-				break;
-			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
-				break;
-			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
-				break;
-			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
-			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
-				break;
-		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
-	}
+	if (seq->accessMethod)
+		seqamid = get_seq_am_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
+	seqam = GetSeqAmRoutineByAMId(seqamid);
+	/* Build column definitions. */
+	seqcols = BuildSeqColumnDefList(seqam->StateTypeOid);
 
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
+	stmt->tableElts = seqcols;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	/* Build new sequence tuple and store it. */
+	tuple = build_seq_tuple(rel, seqam, &new, new.start_value);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +273,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +287,89 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
+	TupleDesc			tupDesc;
+	Datum	   values[SEQ_COL_LASTCOL];
+	bool	   nulls[SEQ_COL_LASTCOL];
+	SeqAmRoutine *seqam;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
-	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tupDesc = RelationGetDescr(seqrel);
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(seqrel, seq, seq->start_value,
+											  true, false);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
+}
+
+/*
+ * Build sequence tuple based on the sequence form and fill in the
+ * sequence AM specific info as well.
+ */
+static HeapTuple
+build_seq_tuple(Relation rel, SeqAmRoutine *seqam, Form_pg_sequence new,
+				int64 restart_value)
+{
+	TupleDesc	tupDesc;
+	HeapTuple	tuple;
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+
+	tupDesc = RelationGetDescr(rel);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new->start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new->increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new->max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new->min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new->cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new->is_cycled);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(rel, new, restart_value,
+											  false, true);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	return tuple;
 }
 
 /*
@@ -361,7 +412,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +432,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,6 +440,69 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 }
 
 /*
+ * Replace the type of amstate column.
+ *
+ * We don't do AlterTable here as that produces dead columns which we don't
+ * want. This is safe because the sequence page is controlled by code in this
+ * module and isn't changed the same way as a table.
+ *
+ * TODO: check if anybody is depending on the row-type associated with the
+ * sequence.
+ */
+static void
+replace_sequence_amstate_col(Oid seqrelid, Oid typid)
+{
+	Relation	attr_rel;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	HeapTuple	tp,
+				attr_tuple,
+				newattr_tuple;
+	Form_pg_type	typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+
+	memset(nulls, 0, sizeof(nulls));
+	memset(replace, 0, sizeof(replace));
+
+	replace[Anum_pg_attribute_atttypid - 1] = true;
+	replace[Anum_pg_attribute_attlen - 1] = true;
+	replace[Anum_pg_attribute_attbyval - 1] = true;
+	replace[Anum_pg_attribute_attalign - 1] = true;
+
+	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(typid);
+	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(typtup->typlen);
+	values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(typtup->typbyval);
+	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(typtup->typalign);
+
+	/* Build DROP command for amstate of old AM. */
+	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+	attr_tuple = SearchSysCache2(ATTNUM,
+								 ObjectIdGetDatum(seqrelid),
+								 Int16GetDatum(SEQ_COL_AMSTATE));
+	if (!HeapTupleIsValid(attr_tuple))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 SEQ_COL_AMSTATE, seqrelid);
+
+	newattr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel),
+									  values, nulls, replace);
+	simple_heap_update(attr_rel, &newattr_tuple->t_self, newattr_tuple);
+	CatalogUpdateIndexes(attr_rel, newattr_tuple);
+
+	ReleaseSysCache(tp);
+	heap_freetuple(newattr_tuple);
+	ReleaseSysCache(attr_tuple);
+
+	heap_close(attr_rel, RowExclusiveLock);
+}
+
+/*
  * AlterSequence
  *
  * Modify the definition of a sequence relation
@@ -406,19 +510,24 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence seq,
+					 new;
 	List	   *owned_by;
 	ObjectAddress address;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
+	SeqAmRoutine *oldseqam;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +535,187 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
+	oldamid = seqrel->rd_rel->relam;
+	oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	if (stmt->accessMethod)
+		seqamid = get_seq_am_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(stmt->options, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		HeapTuple       seqamtup;
+		Form_pg_am		form_am;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
+		SeqAmRoutine	   *newseqam;
+
+		oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = oldseqam->Alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
+
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(AMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
+
+		newseqam = GetSeqAmRoutineByAMId(seqamid);
+
+		form_am = (Form_pg_am) GETSTRUCT(seqamtup);
+
+		validnsps[0] = NameStr(form_am->amname);
+		validnsps[1] = NULL;
+
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		(void) am_reloptions(newseqam->amoptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
 
-		PageSetLSN(page, recptr);
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * And change the type definition.
+		 *
+		 * We can't use AlterTable internals here because the sequence
+		 * has to have the expected number of columns and no
+		 * attisdropped = true columns.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		replace_sequence_amstate_col(seqrelid, newseqam->StateTypeOid);
+		CommandCounterIncrement();
+
+		/* Rebuild the sequence tuple and save it. */
+		tuple = build_seq_tuple(seqrel, newseqam, new, restart_value);
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										AccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = AccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
+	else
+	{
+		Datum	   newamstate;
 
-	END_CRIT_SECTION();
+		sequence_check_range(restart_value, new->min_value, new->max_value,
+							 restart_requested ? "RESTART" : "START");
 
-	UnlockReleaseBuffer(buf);
+		/* Let the new sequence AM initialize. */
+		newamstate = oldseqam->Init(seqrel, new, restart_value,
+									restart_requested, false);
+
+		sequence_start_update(&seqh, true);
+		memcpy(seq, new, sizeof(FormData_pg_sequence));
+		sequence_save_state(&seqh, newamstate, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +756,26 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +800,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam->Alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +817,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for syncrep.
-	 * It's sufficient to ensure the toplevel transaction has an xid, no need
-	 * to assign xids subxacts, that'll already trigger an appropriate wait.
-	 * (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +828,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +889,26 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,106 +925,86 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
+	seqam->Setval(seqrel, &seqh, next);
 
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
-
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	last_used_seq = elm;
 
-	UnlockReleaseBuffer(buf);
+	sequence_close(&seqh);
 
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	LocalSequenceState	state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
-	do_setval(relid, next, true);
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
+
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Build the state and pass it to sequence AM. */
+	state.last_value = next;
+	state.log_cnt = 0;
+	state.is_called = iscalled;
+	seqam->SetState(seqh.rel, &seqh, PointerGetDatum(&state));
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1034,11 +1063,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1118,59 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->statetyp = InvalidOid;
+	seqh->statetyplen = -1;
+	seqh->statetypbyval = false;
+	seqh->statetypalign = '\0';
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(!seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1180,181 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+Form_pg_sequence
+sequence_read_options(SequenceHandle *seqh)
+{
+	return (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
+}
+
+Datum
+sequence_read_state(SequenceHandle *seqh)
+{
+	HeapTuple		tuple = sequence_read_tuple(seqh);
+	bool			isnull;
+	TupleDesc		tupDesc;
+	Datum			res;
+
+	tupDesc = RelationGetDescr(seqh->rel);
+
+	res = fastgetattr(tuple, SEQ_COL_AMSTATE, tupDesc, &isnull);
+	Assert(!isnull);
+
+	return res;
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool dowal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+	if (seqh->statetyp == InvalidOid)
+	{
+		SeqAmRoutine *seqam = GetSeqAmRoutineForRelation(seqh->rel);
+		seqh->statetyp = seqam->StateTypeOid;
+
+		get_typlenbyvalalign(seqh->statetyp, &seqh->statetyplen,
+							 &seqh->statetypbyval, &seqh->statetypalign);
+		Assert(seqh->statetyplen > 0);
+	}
+
+	if (dowal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_save_state(SequenceHandle *seqh, Datum amstate, bool dowal)
+{
+	char	   *seqstate = DatumGetPointer(sequence_read_state(seqh));
+	Page		page;
+
+	Assert(seqh->inupdate);
+
+
+	/*
+	 * Update the state data inline.
+	 *
+	 * This is only needed when the provided amstate datum points to different
+	 * data than what is already in the tuple.
+	 */
+	if (DatumGetPointer(amstate) != seqstate)
+	{
+		if (seqh->statetypbyval)
+			store_att_byval(seqstate, amstate, seqh->statetyplen);
+		else
+			memmove(seqstate, DatumGetPointer(amstate), seqh->statetyplen);
+	}
+
+	page = BufferGetPage(seqh->buf);
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (dowal && RelationNeedsWAL(seqh->rel))
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	Assert(BufferIsValid(seqh->buf));
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1364,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1441,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1449,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1458,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1466,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1473,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1486,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1539,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1553,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1663,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1691,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1699,85 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Datum		state;
+	char	   *statestr;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typoutput;
+	bool		typisvarlena;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type output function. */
+	getTypeOutputInfo(seqam->StateTypeOid, &typoutput, &typisvarlena);
+
+	/* Get the output and convert it to string. */
+	state = seqam->GetState(seqh.rel, &seqh);
+	statestr = OidOutputFunctionCall(typoutput, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_TEXT_P(cstring_to_text(statestr));
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *statestr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typinput,
+				typioparam;
+	Datum		state;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type input function. */
+	getTypeInputInfo(seqam->StateTypeOid, &typinput, &typioparam);
+
+	/* Convert the string to the state type and set it as new state. */
+	state = OidInputFunctionCall(typinput, statestr, typioparam, -1);
+	seqam->SetState(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1843,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 96dc923..6e6ee5d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -269,6 +270,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -454,7 +456,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -473,7 +475,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -551,13 +552,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		SeqAmRoutine *seqam;
+
+		Assert(relamid != InvalidOid);
+		seqam = GetSeqAmRoutineByAMId(relamid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 NULL, true, false);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		(void) am_reloptions(seqam->amoptions, reloptions, true);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -678,6 +695,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3308,7 +3326,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW |
+								ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4308,6 +4327,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4351,8 +4373,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a table, view, or foreign table");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9403,12 +9425,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqamroutine->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
+					 errmsg("\"%s\" is not a table, view, materialized view, index, sequence, or TOAST table",
 							RelationGetRelationName(rel))));
 			break;
 	}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 71d4df9..ca53b8c 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2116,7 +2116,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e9d9ba2..ca0f481 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f4e4a91..9ea0eb8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3565,7 +3565,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3578,7 +3580,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 854c062..5972ca6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1627,7 +1627,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1638,7 +1640,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1273352..75af985 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -310,7 +310,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <list>	event_trigger_when_list event_trigger_value_list
 %type <defelt>	event_trigger_when_item
-%type <chr>		enable_trigger
+%type <chr>		enable_trigger am_type
 
 %type <str>		copy_file_name
 				database_name access_method_clause access_method attr_name
@@ -605,7 +605,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 METHOD 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
@@ -3587,7 +3588,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3609,7 +3636,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3617,11 +3668,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3680,7 +3754,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
@@ -4715,16 +4789,21 @@ row_security_cmd:
  *
  *****************************************************************************/
 
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
 				{
 					CreateAmStmt *n = makeNode(CreateAmStmt);
 					n->amname = $4;
 					n->handler_name = $8;
-					n->amtype = AMTYPE_INDEX;
+					n->amtype = $6;
 					$$ = (Node *) n;
 				}
 		;
 
+am_type:
+			INDEX		{ $$ = AMTYPE_INDEX; }
+		|	SEQUENCE	{ $$ = AMTYPE_SEQUENCE; }
+		;
+
 /*****************************************************************************
  *
  *		QUERIES :
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6528494..509fa32 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -29,6 +29,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -456,6 +457,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4d0aac9..8b95577 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -965,7 +965,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -998,7 +999,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index dd447cf..6c1456e 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -399,6 +399,31 @@ index_am_handler_out(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();			/* keep compiler quiet */
 }
 
+/*
+ * seq_am_handler_int		- input routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_in(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * seq_am_handler_out		- output routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_out(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot display a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
 
 /*
  * tsm_handler_in		- input routine for pseudo-type TSM_HANDLER.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 130c06d..b9686da 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -260,6 +261,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -424,6 +426,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	amoptions_function	amoptions = NULL;
 
 	relation->rd_options = NULL;
 
@@ -432,10 +435,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_amroutine->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqamroutine->amoptions;
+			break;
 		default:
 			return;
 	}
@@ -447,8 +455,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_amroutine->amoptions : NULL);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1049,11 +1056,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1555,6 +1565,34 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation relation)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	SeqAmRoutine	   *result, *tmp;
+
+	/*
+	 * Look up the index's access method, save the OID of its handler function
+	 */
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 relation->rd_rel->relam);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amtype == AMTYPE_SEQUENCE);
+	relation->rd_amhandler = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	result = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												 sizeof(SeqAmRoutine));
+	tmp = GetSeqAmRoutine(relation->rd_amhandler);
+	memcpy(result, tmp, sizeof(SeqAmRoutine));
+	relation->rd_seqamroutine = result;
+}
 
 /*
  *		formrdesc
@@ -4885,6 +4923,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqamroutine == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 06cb166..fa4d39d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqamapi.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 454225d..6e6f5a0 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4747,6 +4747,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4798,6 +4799,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4840,6 +4842,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4882,6 +4885,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4924,6 +4928,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4964,6 +4969,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5003,6 +5009,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5042,6 +5049,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5081,6 +5089,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5119,6 +5128,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5153,6 +5163,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5182,6 +5193,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5221,6 +5233,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5274,6 +5287,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5339,6 +5353,10 @@ getTables(Archive *fout, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -11577,6 +11595,9 @@ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
 		case AMTYPE_INDEX:
 			appendPQExpBuffer(q, "TYPE INDEX ");
 			break;
+		case AMTYPE_SEQUENCE:
+			appendPQExpBuffer(q, "TYPE SEQUENCE ");
+			break;
 		default:
 			write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
 					  aminfo->amtype, qamname);
@@ -15317,7 +15338,8 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -15334,8 +15356,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	if (fout->remoteVersion >= 80400)
 	{
 		appendPQExpBuffer(query,
-						  "SELECT sequence_name, "
-						  "start_value, increment_by, "
+						  "SELECT start_value, increment_by, "
 				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
 				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
 						  "     ELSE max_value "
@@ -15351,8 +15372,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	else
 	{
 		appendPQExpBuffer(query,
-						  "SELECT sequence_name, "
-						  "0 AS start_value, increment_by, "
+						  "SELECT 0 AS start_value, increment_by, "
 				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
 				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
 						  "     ELSE max_value "
@@ -15377,24 +15397,40 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 		exit_nicely(1);
 	}
 
-	/* Disable this check: it fails if sequence has been renamed */
-#ifdef NOT_USED
-	if (strcmp(PQgetvalue(res, 0, 0), tbinfo->dobj.name) != 0)
+	startv = PQgetvalue(res, 0, 0);
+	incby = PQgetvalue(res, 0, 1);
+	if (!PQgetisnull(res, 0, 2))
+		maxv = PQgetvalue(res, 0, 2);
+	if (!PQgetisnull(res, 0, 3))
+		minv = PQgetvalue(res, 0, 3);
+	cache = PQgetvalue(res, 0, 4);
+	cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0);
+
+	/* 9.6 adds sequence access methods */
+	if (fout->remoteVersion >= 90600)
 	{
-		write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
-				  tbinfo->dobj.name, PQgetvalue(res, 0, 0));
-		exit_nicely(1);
-	}
-#endif
+		PGresult   *res2;
 
-	startv = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
-	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
-	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
-	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
+		printfPQExpBuffer(query, "SELECT a.amname\n"
+								 "FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
 
 	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
@@ -15437,6 +15473,13 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -15503,6 +15546,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15519,16 +15565,23 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/* On 9.6 there is special interface for dumping sequences */
+	if (fout->remoteVersion >= 90600)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15541,14 +15594,27 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90600)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index c02c536..c62abcc 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -231,6 +231,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_class) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 7b2f4e6..ac85da3 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1403,30 +1403,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1470,13 +1446,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1805,6 +1823,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1814,12 +1834,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.amname\n"
+								"FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 688d92a..79263e9 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1537,7 +1537,7 @@ psql_completion(const char *text, int start, int end)
 	{
 		static const char *const list_ALTERSEQUENCE[] =
 		{"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
-		"SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", NULL};
+		"SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", "USING", NULL};
 
 		COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
 	}
@@ -2116,8 +2116,8 @@ psql_completion(const char *text, int start, int end)
 /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */
 	else if (TailMatches3("CREATE", "SEQUENCE", MatchAny) ||
 			 TailMatches4("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny))
-		COMPLETE_WITH_LIST8("INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", "CACHE",
-							"CYCLE", "OWNED BY", "START WITH");
+		COMPLETE_WITH_LIST9("INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", "CACHE",
+							"CYCLE", "OWNED BY", "START WITH", "USING");
 	else if (TailMatches4("CREATE", "SEQUENCE", MatchAny, "NO") ||
 		TailMatches5("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO"))
 		COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE");
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 469ac67..69135aa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -273,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
+extern bytea *am_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqamapi.h b/src/include/access/seqamapi.h
new file mode 100644
index 0000000..a4c826a
--- /dev/null
+++ b/src/include/access/seqamapi.h
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqamapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAMAPI_H
+#define SEQAMAPI_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef bytea * (*ParseRelOptions_function) (Datum reloptions,
+											 bool validate);
+
+typedef Datum (*SeqAMInit_function) (Relation seqrel,
+									 Form_pg_sequence seq,
+									 int64 restart_value,
+									 bool restart_requested,
+									 bool is_init);
+
+typedef int64 (*SeqAMAlloc_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 nrequested,
+									  int64 *last);
+
+typedef void (*SeqAMSetval_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 new_value);
+
+typedef Datum (*SeqAMGetState_function) (Relation seqrel,
+										 SequenceHandle *seqh);
+typedef void (*SeqAMSetState_function) (Relation seqrel, SequenceHandle *seqh,
+										Datum amstate);
+
+typedef struct SeqAmRoutine
+{
+	NodeTag		type;
+
+	/* Custom columns needed by the AM */
+	Oid			StateTypeOid;
+
+	/* Function for parsing reloptions */
+	ParseRelOptions_function	amoptions;
+
+	/* Initialization */
+	SeqAMInit_function			Init;
+
+	/* nextval handler */
+	SeqAMAlloc_function			Alloc;
+
+	/* State manipulation functions */
+	SeqAMSetval_function		Setval;
+	SeqAMGetState_function		GetState;
+	SeqAMSetState_function		SetState;
+} SeqAmRoutine;
+
+extern SeqAmRoutine *GetSeqAmRoutine(Oid seqamhandler);
+extern SeqAmRoutine *GetSeqAmRoutineByAMId(Oid amoid);
+extern SeqAmRoutine *GetSeqAmRoutineForRelation(Relation relation);
+extern void ValidateSeqAmRoutine(SeqAmRoutine *routine);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern Form_pg_sequence sequence_read_options(SequenceHandle *seqh);
+extern Datum sequence_read_state(SequenceHandle *seqh);
+extern void sequence_start_update(SequenceHandle *seqh, bool dowal);
+extern void sequence_save_state(SequenceHandle *seqh, Datum seqstate,
+								bool dowal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+/* We need this public for serval3 */
+typedef struct LocalSequenceState
+{
+	int64   last_value;
+	int32	log_cnt;
+	bool	is_called;
+} LocalSequenceState;
+
+extern Datum seqam_local_state_in(PG_FUNCTION_ARGS);
+extern Datum seqam_local_state_out(PG_FUNCTION_ARGS);
+extern Datum seqam_local_handler(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAMAPI_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..d6b3cd7 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 1116923..9c615f7 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
  * ----------------
  */
 #define AMTYPE_INDEX					'i'		/* index access method */
+#define	AMTYPE_SEQUENCE					'S'		/* sequence access method */
 
 /* ----------------
  *		initial contents of pg_am
@@ -84,4 +85,8 @@ DATA(insert OID = 3580 (  brin		brinhandler i ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
+DATA(insert OID = 6023 (  local		seqam_local_handler	S ));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6023
+
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c86b920..54a712c 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1759,6 +1759,10 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v u 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 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 25 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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 s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3690,6 +3694,10 @@ DATA(insert OID = 326  (  index_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f
 DESCR("I/O");
 DATA(insert OID = 327  (  index_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
 DESCR("I/O");
+DATA(insert OID = 6021  (  seq_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 6020 "2275" _null_ _null_ _null_ _null_ _null_ seq_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6022 (  seq_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6020" _null_ _null_ _null_ _null_ _null_ seq_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
 DATA(insert OID = 3311 (  tsm_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 3312 (  tsm_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
@@ -3701,6 +3709,10 @@ DESCR("BERNOULLI tablesample method handler");
 DATA(insert OID = 3314 (  system			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_system_handler _null_ _null_ _null_ ));
 DESCR("SYSTEM tablesample method handler");
 
+/* sequence access method handlers */
+DATA(insert OID = 6024 (  seqam_local_handler	   PGNSP PGUID 12 1 0 0 0 f f f f f f v s 1 0 6020 "2281" _null_ _null_ _null_ _null_ _null_ seqam_local_handler _null_ _null_ _null_ ));
+DESCR("Local SequenceAM handler");
+
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
 DESCR("MD5 hash");
@@ -5270,6 +5282,13 @@ DESCR("pg_controldata recovery state information as a function");
 DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
 DESCR("pg_controldata init state information as a function");
 
+/* Sequence AM */
+DATA(insert OID = 6027 (  seqam_local_state_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 6025 "2275" _null_ _null_ _null_ _null_ _null_ seqam_local_state_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6028 (  seqam_local_state_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6025" _null_ _null_ _null_ _null_ _null_ seqam_local_state_out _null_ _null_ _null_ ));
+DESCR("I/O");
+
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2c90b76..7f020fe 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -696,11 +696,18 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 #define FDW_HANDLEROID	3115
 DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define INDEX_AM_HANDLEROID	325
+DATA(insert OID = 6020 ( seq_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 seq_am_handler_in seq_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQ_AM_HANDLEROID	6020
 DATA(insert OID = 3310 ( tsm_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define TSM_HANDLEROID	3310
 DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 #define ANYRANGEOID		3831
 
+/* Sequence AM composite types */
+DATA(insert OID = 6025 ( seqam_local_state	PGNSP PGUID 16 f b U f t \054 0 0 6026 seqam_local_state_in seqam_local_state_out - - - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQAMLOCALSTATEOID 6025
+DATA(insert OID = 6026 ( _seqam_local_state	PGNSP PGUID -1 f b A f t \054 0 6025 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 
 /*
  * macros
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index b064eb4..c9bf431 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -139,6 +139,7 @@ extern Datum transformGenericOptions(Oid catalogId,
 extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
 extern void RemoveAccessMethodById(Oid amOid);
 extern Oid	get_index_am_oid(const char *amname, bool missing_ok);
+extern Oid	get_seq_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);
 
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..2ff89b4 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -19,20 +19,17 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "storage/relfilenode.h"
-
+#include "access/htup_details.h"
 
 typedef struct FormData_pg_sequence
 {
-	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
+	/* amstate follows */
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,19 +38,16 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  * Columns of a sequence relation
  */
 
-#define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_STARTVAL		1
+#define SEQ_COL_INCBY			2
+#define SEQ_COL_MAXVALUE		3
+#define SEQ_COL_MINVALUE		4
+#define SEQ_COL_CACHE			5
+#define SEQ_COL_CYCLE			6
+#define SEQ_COL_AMSTATE			7
 
-#define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_FIRSTCOL		SEQ_COL_STARTVAL
+#define SEQ_COL_LASTCOL			SEQ_COL_AMSTATE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +66,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 7a770f4..1bceec8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 734df77..752d3a1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -477,6 +477,7 @@ typedef enum NodeTag
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
+	T_SeqAmRoutine,				/* in access/seqamapi.h */
 	T_TsmRoutine				/* in access/tsmapi.h */
 } NodeTag;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8b958b4..859fbad 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2205,8 +2205,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2214,8 +2216,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index d5c4b01..cf7e01b 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -587,6 +587,8 @@ extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
 extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_in(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_out(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
 extern Datum internal_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f2bebf2..4fe7e52 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -106,13 +106,18 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	Oid			rd_amhandler;	/* OID of access method's handler function */
+
+	/* These are non-NULL only for a sequence relation */
+	struct SeqAmRoutine *rd_seqamroutine;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
@@ -126,7 +131,6 @@ typedef struct RelationData
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
-	Oid			rd_amhandler;	/* OID of index AM's handler function */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
 	/* use "struct" here to avoid needing to include amapi.h: */
 	struct IndexAmRoutine *rd_amroutine;		/* index AM's API struct */
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index 47d6024..9242982 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -106,3 +106,22 @@ drop cascades to index grect2ind
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+-- Checks for sequence access methods
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+ERROR:  cannot drop access method local2 because other objects depend on it
+DETAIL:  sequence test_seqam depends on access method local2
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ae50c94..f1b1fc2 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -129,8 +129,9 @@ NOTICE:  view "v12_temp" will be a temporary view
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 NOTICE:  view "v13_temp" will be a temporary view
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index a8dc0c1..1c51a5e 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1586,7 +1586,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
  oid | amname | oid | proname 
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3a19cde 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate 
+-------------+--------------+---------------------+-----------+-------------+-----------+---------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (1,f,0)
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate  
+-------------+--------------+---------------------+-----------+-------------+-----------+----------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (2,t,31)
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c5dfbb5..a30582e 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,52 @@ SELECT table_name, column_name, is_updatable
   FROM information_schema.columns
  WHERE table_name LIKE E'r_\\_view%'
  ORDER BY table_name, ordinal_position;
- table_name |  column_name  | is_updatable 
-------------+---------------+--------------
- ro_view1   | a             | NO
- ro_view1   | b             | NO
- ro_view10  | a             | NO
- ro_view11  | a             | NO
- ro_view11  | b             | NO
- ro_view12  | a             | NO
- ro_view13  | a             | NO
- ro_view13  | b             | NO
- ro_view17  | a             | NO
- ro_view17  | b             | NO
- ro_view18  | a             | NO
- ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
- ro_view19  | start_value   | NO
- ro_view19  | increment_by  | NO
- ro_view19  | max_value     | NO
- ro_view19  | min_value     | NO
- ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
- ro_view19  | is_cycled     | NO
- ro_view19  | is_called     | NO
- ro_view2   | a             | NO
- ro_view2   | b             | NO
- ro_view20  | a             | NO
- ro_view20  | b             | NO
- ro_view20  | g             | NO
- ro_view3   | ?column?      | NO
- ro_view4   | count         | NO
- ro_view5   | a             | NO
- ro_view5   | rank          | NO
- ro_view6   | a             | NO
- ro_view6   | b             | NO
- ro_view7   | a             | NO
- ro_view7   | b             | NO
- ro_view8   | a             | NO
- ro_view8   | b             | NO
- ro_view9   | a             | NO
- ro_view9   | b             | NO
- rw_view14  | ctid          | NO
- rw_view14  | a             | YES
- rw_view14  | b             | YES
- rw_view15  | a             | YES
- rw_view15  | upper         | NO
- rw_view16  | a             | YES
- rw_view16  | b             | YES
- rw_view16  | aa            | YES
-(46 rows)
+ table_name | column_name  | is_updatable 
+------------+--------------+--------------
+ ro_view1   | a            | NO
+ ro_view1   | b            | NO
+ ro_view10  | a            | NO
+ ro_view11  | a            | NO
+ ro_view11  | b            | NO
+ ro_view12  | a            | NO
+ ro_view13  | a            | NO
+ ro_view13  | b            | NO
+ ro_view17  | a            | NO
+ ro_view17  | b            | NO
+ ro_view18  | a            | NO
+ ro_view19  | start_value  | NO
+ ro_view19  | increment_by | NO
+ ro_view19  | max_value    | NO
+ ro_view19  | min_value    | NO
+ ro_view19  | cache_value  | NO
+ ro_view19  | is_cycled    | NO
+ ro_view19  | amstate      | NO
+ ro_view2   | a            | NO
+ ro_view2   | b            | NO
+ ro_view20  | a            | NO
+ ro_view20  | b            | NO
+ ro_view20  | g            | NO
+ ro_view3   | ?column?     | NO
+ ro_view4   | count        | NO
+ ro_view5   | a            | NO
+ ro_view5   | rank         | NO
+ ro_view6   | a            | NO
+ ro_view6   | b            | NO
+ ro_view7   | a            | NO
+ ro_view7   | b            | NO
+ ro_view8   | a            | NO
+ ro_view8   | b            | NO
+ ro_view9   | a            | NO
+ ro_view9   | b            | NO
+ rw_view14  | ctid         | NO
+ rw_view14  | a            | YES
+ rw_view14  | b            | YES
+ rw_view15  | a            | YES
+ rw_view15  | upper        | NO
+ rw_view16  | a            | YES
+ rw_view16  | b            | YES
+ rw_view16  | aa           | YES
+(43 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index e2051c5..e811ea3 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -71,3 +71,19 @@ DROP ACCESS METHOD gist2 CASCADE;
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+
+-- Checks for sequence access methods
+
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 58d361d..e967fff 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -131,8 +131,9 @@ CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 6c9784a..6c44d70 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1055,7 +1055,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
 
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..c2b1164 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
0002-gapless-seq-2016-04-04.patchtext/x-diff; charset=US-ASCII; name=0002-gapless-seq-2016-04-04.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
index 25263c0..991ee7b 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -20,6 +20,7 @@ SUBDIRS = \
 		earthdistance	\
 		file_fdw	\
 		fuzzystrmatch	\
+		gapless_seq	\
 		hstore		\
 		intagg		\
 		intarray	\
diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..130104a
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+ERROR:  cannot drop access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..99d6021
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,58 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_handler(internal)
+RETURNS SEQ_AM_HANDLER
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_in(cstring)
+RETURNS seqam_gapless_state
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_out(seqam_gapless_state)
+RETURNS cstring
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TYPE seqam_gapless_state (
+	INPUT   = seqam_gapless_state_in,
+	OUTPUT  = seqam_gapless_state_out,
+	INTERNALLENGTH = 16,
+	ALIGNMENT = double
+);
+COMMENT ON TYPE seqam_gapless_state IS 'state for gapless sequence am';
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	last_value bigint NOT NULL,
+	is_called bool NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relkind = 'S' AND relam = (
+			SELECT oid FROM pg_am WHERE amname = 'gapless' AND amtype = 'S'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD gapless TYPE SEQUENCE HANDLER seqam_gapless_handler;
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..6a97494
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,538 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager / Gapless sequence implementation
+ *
+ *------------------------------------------------------------
+ */
+
+typedef struct GaplessSequenceState
+{
+	int64   last_value;
+	uint32	xid;
+	bool	is_called;
+} GaplessSequenceState;
+
+typedef struct GaplessSequence
+{
+	FormData_pg_sequence	seq;
+	GaplessSequenceState	state;
+} GaplessSequence;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	int64	last_value;
+	bool	is_called;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static bytea *seqam_gapless_reloptions(Datum reloptions, bool validate);
+static Datum seqam_gapless_init(Relation seqrel, Form_pg_sequence seq,
+								int64 restart_value, bool restart_requested,
+								bool is_init);
+static int64 seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh,
+								 int64 nrequested, int64 *last);
+static void seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh,
+								 int64 new_value);
+static Datum seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+									Datum amstate);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_handler);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_in);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_out);
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+							   int64 last_value, bool is_called);
+
+Datum
+seqam_gapless_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr, *end;
+	Datum		d;
+	GaplessSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	end = ptr;
+	while (*end != ',' && *end != ')')
+	{
+		char		ch = *end++;
+
+		if (ch == '\0')
+			goto malformed;
+	}
+
+	d = DirectFunctionCall1(int8in,
+							CStringGetDatum(pnstrdup(ptr, end-ptr)));
+	state->last_value = DatumGetInt64(d);
+
+	/* is_called is optional */
+	if (*end == ',')
+	{
+		ptr = ++end;
+
+		end = state_str+strlen(state_str)-1;
+
+		if (*end != ')')
+			goto malformed;
+
+		d = DirectFunctionCall1(boolin,
+								CStringGetDatum(pnstrdup(ptr, end-ptr)));
+		state->is_called = DatumGetBool(d);
+	}
+	else
+		state->is_called = true;
+
+	state->xid = InvalidTransactionId;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_gapless_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_gapless_state_out(PG_FUNCTION_ARGS)
+{
+	GaplessSequenceState   *state;
+	StringInfoData			buf;
+
+	state = (GaplessSequenceState *) PG_GETARG_POINTER(0);
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("INT64_FORMAT",%s)",
+					 state->last_value, state->is_called ? "t" : "f");
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the gapless sequence access method.
+ */
+Datum
+seqam_gapless_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine   *seqam = makeNode(SeqAmRoutine);
+	Oid				nspid,
+					typid;
+
+	nspid = get_namespace_oid("gapless_seq", false);
+	typid = GetSysCacheOid2(TYPENAMENSP,
+							PointerGetDatum("seqam_gapless_state"),
+							ObjectIdGetDatum(nspid));
+
+	if (!OidIsValid(typid))
+		elog(ERROR, "cache lookup failed for type \"seqam_gapless_state\"");
+
+	seqam->StateTypeOid = typid;
+	seqam->amoptions = seqam_gapless_reloptions;
+	seqam->Init = seqam_gapless_init;
+	seqam->Alloc = seqam_gapless_alloc;
+	seqam->Setval = seqam_gapless_setval;
+	seqam->GetState = seqam_gapless_get_state;
+	seqam->SetState = seqam_gapless_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+static bytea *
+seqam_gapless_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+static Datum
+seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	Oid			seqrelid = seqrel->rd_id;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	GaplessSequenceState   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(GaplessSequenceState));
+	else
+		seqstate = &((GaplessSequence *) seq)->state;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->xid = GetTopTransactionId();
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, valtuple, seqstate->last_value,
+						   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+static int64
+seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+					int64 *last)
+{
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	Form_pg_sequence		seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = &((GaplessSequence *) seq)->state;
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, result, true);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* We always WAL log for gapless sequence. */
+	sequence_start_update(seqh, true);
+	seqstate->last_value = result;
+	seqstate->is_called = true;
+	if (seqstate->xid != local_xid)
+		seqstate->xid = local_xid;
+	sequence_save_state(seqh, PointerGetDatum(seqstate), true);
+	sequence_finish_update(seqh);
+
+	*last = result;
+
+	return result;
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+static void
+seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+static Datum
+seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	GaplessSequenceState   *seqstate = palloc0(sizeof(GaplessSequenceState));
+
+	/*
+	 * Get the last value from the values table, if not found use the values
+	 * from sequence typle.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		seqstate->last_value = v->last_value;
+		seqstate->is_called = v->is_called;
+	}
+	else
+	{
+		Datum		amstate = sequence_read_state(seqh);
+
+		memcpy(seqstate, DatumGetPointer(amstate),
+			   sizeof(GaplessSequenceState));
+		sequence_release_tuple(seqh);
+	}
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+static void
+seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+						Datum amstate)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	FormData_pg_sequence   *seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(seqstate->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, seqstate->last_value,
+					   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	GaplessSequenceState   *seqstate;
+	Datum					seqstated = sequence_read_state(seqh);
+
+	seqstate = (GaplessSequenceState *) DatumGetPointer(seqstated);
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (seqstate->xid != local_xid &&
+		   TransactionIdIsInProgress(seqstate->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seqstate->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seqstated = sequence_read_state(seqh);
+		seqstate = (GaplessSequenceState *) DatumGetPointer(seqstated);
+	}
+
+	return sequence_read_options(seqh);
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+				   int64 last_value, bool is_called)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..b49cec6
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index c8708ec..0413e67 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -117,6 +117,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
  &earthdistance;
  &file-fdw;
  &fuzzystrmatch;
+ &gapless-seq;
  &hstore;
  &intagg;
  &intarray;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index e2bf725..0a4e728 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -121,6 +121,7 @@
 <!ENTITY earthdistance   SYSTEM "earthdistance.sgml">
 <!ENTITY file-fdw        SYSTEM "file-fdw.sgml">
 <!ENTITY fuzzystrmatch   SYSTEM "fuzzystrmatch.sgml">
+<!ENTITY gapless-seq     SYSTEM "gapless-seq.sgml">
 <!ENTITY hstore          SYSTEM "hstore.sgml">
 <!ENTITY intagg          SYSTEM "intagg.sgml">
 <!ENTITY intarray        SYSTEM "intarray.sgml">
diff --git a/doc/src/sgml/gapless-seq.sgml b/doc/src/sgml/gapless-seq.sgml
new file mode 100644
index 0000000..44cc224
--- /dev/null
+++ b/doc/src/sgml/gapless-seq.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless-seq" xreflabel="gapless_seq">
+ <title>gapless_seq</title>
+
+ <indexterm zone="gapless-seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence and hold
+  lock on the sequence itself, so any concurrent nextval() calls have to wait
+  for the transaction to finish. This could be very costly and is not
+  recommended for general usage except in specific applications that require
+  this feature.
+ </para>
+
+</sect1>
#123Petr Jelinek
petr@2ndquadrant.com
In reply to: Fabrízio de Royes Mello (#122)
Re: Sequence Access Method WIP

On 04/04/16 15:53, Fabr�zio de Royes Mello wrote:

On Thu, Mar 31, 2016 at 9:19 AM, Petr Jelinek <petr@2ndquadrant.com
<mailto:petr@2ndquadrant.com>> wrote:

Hi,

new version attached that should fix the issue. It was alignment -

honestly don't know what I was thinking using fixed alignment when the
AMs can define their own type.

Yeah... now all works fine... I had a minor issue when apply to the
current master but the attached fix it and I also added tab-complete
support for CREATE SEQUENCE...USING and ALTER SEQUENCE...USING.

Cool thanks.

I ran all the regression tests and all passed.

I have just one question about de 0002 patch:
- There are some reason to not use TransactionId instead of uint32 in
GaplessSequenceState struct?

I missed we have that :)

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#124Fabrízio de Royes Mello
fabriziomello@gmail.com
In reply to: Petr Jelinek (#123)
2 attachment(s)
Re: Sequence Access Method WIP

On Tue, Apr 5, 2016 at 12:52 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:

I have just one question about de 0002 patch:
- There are some reason to not use TransactionId instead of uint32 in
GaplessSequenceState struct?

I missed we have that :)

Attached fix it and also I changed to use TransactionIdEquals instead of
compare xids directly.

- if (seqstate->xid != local_xid)
+ if (!TransactionIdEquals(seqstate->xid, local_xid))

IMHO this patch is Ready for Commiter. Changed status in CommitfestApp.

Regards,

--
Fabrízio de Royes Mello
Consultoria/Coaching PostgreSQL

Show quoted text

Timbira: http://www.timbira.com.br
Blog: http://fabriziomello.github.io
Linkedin: http://br.linkedin.com/in/fabriziomello
Twitter: http://twitter.com/fabriziomello
Github: http://github.com/fabriziomello

Attachments:

0001-seqam-2016-04-05.patchtext/x-diff; charset=US-ASCII; name=0001-seqam-2016-04-05.patchDownload
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 6c0ad3f..e2bf725 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
 <!ENTITY brin       SYSTEM "brin.sgml">
 <!ENTITY planstats    SYSTEM "planstats.sgml">
 <!ENTITY indexam    SYSTEM "indexam.sgml">
+<!ENTITY seqam      SYSTEM "seqam.sgml">
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 0346d36..6b9bae8 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -252,6 +252,7 @@
   &spgist;
   &gin;
   &brin;
+  &seqam;
   &storage;
   &bki;
   &planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+    SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+    RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
    </varlistentry>
 
    <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
      <para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause changes one or more storage parameters for the sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause resets one or more storage parameters to their defaults.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for the new sequence
+      access method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
index 3c091f8..8676a84 100644
--- a/doc/src/sgml/ref/create_access_method.sgml
+++ b/doc/src/sgml/ref/create_access_method.sgml
@@ -61,7 +61,8 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
     <listitem>
      <para>
       This clause specifies type of access method to define.
-      Only <literal>INDEX</literal> is supported at present.
+      Supported values are <literal>INDEX</literal> for index access methods
+      and <literal>SEQUENCE</literal> for sequence access methods.
      </para>
     </listitem>
    </varlistentry>
@@ -75,7 +76,9 @@ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
       of access method to the core.  The handler function must take single
       argument of type <type>internal</>, and its return type depends on the
       type of access method; for <literal>INDEX</literal> access methods, it
-      must be <type>index_am_handler</type>.
+      must be <type>index_am_handler</type> and for
+      <literal>SEQUENCE</literal> access methods it must be
+      <type>sequence_am_handler</type>.
      </para>
 
      <para>
@@ -114,6 +117,8 @@ CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;
    <member><xref linkend="sql-drop-access-method"></member>
    <member><xref linkend="sql-createopclass"></member>
    <member><xref linkend="sql-createopfamily"></member>
+   <member><xref linkend="index-api"> </member>
+   <member><xref linkend="seqam"></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
+    [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies optional storage parameters for a sequence access
+      method.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..5faeae3
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,312 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+  <para>
+   This chapter describes the interface for writing sequence access methods.
+  </para>
+
+  <para>
+   The sequence access method defines behavior of a sequence which is using it.
+   Mainly how the new values are generated. Each sequence can have different
+   access method in order to behave in a way that makes most sense in the
+   application context.
+  </para>
+
+ <sect1 id="seqam-catalog">
+  <title>Catalog Entries for Sequence Access Method</title>
+
+  <para>
+   Each sequence access method is described by a row in the
+   <structname>pg_am</structname> system catalog (see
+   <xref linkend="catalog-pg-am">).
+  </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+  <title>Sequence Access Method Functions</title>
+
+  <para>
+   The SQL interface of the sequence access method consists of a handler
+   function which has to return special pseudo-type
+   <type>sequence_handler</type>. This function must be written in a compiled
+   language such as C, using the version-1 interface. For details on C
+   language calling conventions and dynamic loading, see
+   <xref linkend="xfunc-c">.
+  </para>
+
+  <para>
+   The actual C structure returned by the handler function is following:
+<programlisting>
+typedef struct SeqAmRoutine
+{
+    NodeTag        type;
+
+    /* Custom datatype used for storing state of the sequence by the AM */
+    Oid            StateTypeOid;
+
+    /* Function for parsing reloptions */
+    ParseRelOptions_function    amoptions;
+
+    /* Initialization */
+    SeqAMInit_function          Init;
+
+    /* nextval handler */
+    SeqAMAlloc_function         Alloc;
+
+    /* State manipulation functions */
+    SeqAMSetval_function        Setval;
+    SeqAMGetState_function      GetState;
+    SeqAMSetState_function      SetState;
+} SeqAmRoutine;
+</programlisting>
+  </para>
+
+  <para>
+   These are the functions implemented by the sequence access method.
+  </para>
+
+  <para>
+   The functions defining a sequence access method are:
+  </para>
+
+  <para>
+<programlisting>
+Datum
+SeqAMInit (Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+           bool restart_requested, bool is_init)
+</programlisting>
+   Initialize the sequence's internal state. This function is called both
+   when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+   is called for an existing sequence or by the
+   <command>TRUNCATE RESTART IDENTITY</> command.
+  </para>
+
+  <para>
+   The <literal>seqrel</> points to relcache record representing the
+   <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+   tuple tuple of the sequence.
+   The <literal>seq</> is in-memory version of the
+   <structname>pg_sequence</structname> tuple itself. The
+   <literal>restart_value</> is value the sequence should start from and the
+   <literal>restart_requested</> gives indication if the
+   <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+   is <literal>true</> when this is new sequence being created or when the
+   access method of a sequence has been changed, otherwise it's
+   <literal>false</>.
+
+   to the options in the <command>CREATE SEQUENCE</> or
+   <command>ALTER SEQUENCE</> statement.
+   <literal>reloptions</> contains parsed reloptions passed to the
+   <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+   The <literal>values</> and <literal>nulls</> describe new tuple for the
+   <structname>pg_sequence</structname> tuple which this function can update
+   as needed.
+  </para>
+
+  <para>
+   This function should return the Datum of a state of the sequence. The type
+   of the Datum is the <structname>StateTypeOid</structname> type.
+  </para>
+
+  <para>
+<programlisting>
+bytea *
+ParseRelOptions (Datum reloptions, bool validate)
+</programlisting>
+   Parse and validate the reloptions array for an sequence.
+   <parameter>reloptions</> is a <type>text</> array containing entries of the
+   form <replaceable>name</><literal>=</><replaceable>value</>.
+   The function should construct a <type>bytea</> value.
+   When <parameter>validate</> is true, the function should report a suitable
+   error message if any of the options are unrecognized or have invalid
+   values; when <parameter>validate</> is false, invalid entries should be
+   silently ignored.  (<parameter>validate</> is false when loading options
+   already stored in <structname>pg_catalog</>; an invalid entry could only
+   be found if the access method has changed its rules for options, and in
+   that case ignoring obsolete entries is appropriate.)
+   It is OK to return NULL if default behavior is wanted.
+  </para>
+
+  <para>
+<programlisting>
+int64
+SeqAMAlloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+            int64 *last)
+</programlisting>
+   Allocate new sequence value(s). The <literal>nrequested</> specifies how
+   many new values should be allocated by this call. Return value is the next
+   allocated value and <literal>last</> should be set to last allocated value.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+   Set the current sequence value the the <literal>new_value</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum SeqAMGetState (Relation seqrel, SequenceHandle *seqh)
+</programlisting>
+   Dump the current state of the sequence. The return value is the Datum of
+   a <structname>StateTypeOid</structname>. This interface is mainly
+   used by <command>pg_dump</command>.
+  </para>
+
+  <para>
+<programlisting>
+void SeqAMSetState (Relation seqrel, SequenceHandle *seqh, Datum amstate)
+</programlisting>
+   Restore state of the sequence based on amstate which is Datum of a
+   <structname>StateTypeOid</structname> type.
+   This interface is mainly used by <command>pg_dump</command>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+  <title>Sequence Access Method Storage API</title>
+
+  <para>
+   To store the current state of the sequence, the backend provides the
+   following helper functions which the sequence access method can use:
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
+</programlisting>
+   Open sequence with given <literal>relid</>. The <literal>seqh</> is
+   output parameter which will be set to the sequence handle.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh)
+</programlisting>
+   Release and close the opened sequence.
+  </para>
+
+  <para>
+<programlisting>
+Form_pg_sequence
+(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns the whole pg_sequence tuple as the
+   <structname>Form_pg_sequence</structname> structure.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+Datum
+sequence_read_state(SequenceHandle *seqh)
+</programlisting>
+   Reads and locks the sequence tuple for processing by the access method.
+   Returns just the sequence access method specific state Datum.
+   The tuple should be released by calling <function>sequence_release_tuple</>.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+</programlisting>
+   Start the sequence update. The <literal>do_wal</> gives hint if at least
+   one of subsequent <function>sequence_apply_update()</> calls will be with
+   <literal>do_wal = true</>.
+</para>
+
+  <para>
+<programlisting>
+void
+sequence_save_state(SequenceHandle *seqh, Datum seqstate, bool dowal)
+</programlisting>
+   Save the modified sequence state tuple indicating if the change should be
+   WAL logged as well as saved to the catalog.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh)
+</programlisting>
+   Finish the sequence update.
+  </para>
+
+  <para>
+  To update the sequence tuple you have to call all three above functions in
+  order to be sure the update is applied in error safe manner. The
+  <function>sequence_save_update()</> can be called multiple times between
+  single pair of <function>sequence_start_update()</> and
+  <function>sequence_finish_update()</> calls. Update function cannot be called
+  if the tuple was already released by the
+  <function>sequence_release_tuple()</> function.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh)
+</programlisting>
+   Release the tuple read and locked by <function>sequence_read_options</>
+   and/or added by <function>sequence_read_state</>.
+  </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+  <title>Sequence Access Method Utility Functions</title>
+
+  <para>
+   Additional utility functions which can be useful when writing sequence
+   access methods:
+  </para>
+
+  <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+</programlisting>
+   Returns <literal>true</> if the sequence tuple was last saved before last
+   checkpoint. This can be help when deciding what to set <literal>do_wal</>
+   to when calling <function>sequence_save_update</>.
+  </para>
+
+  <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                   int64 minv, int64 maxv, int64 incby,
+                   bool is_cycled, bool report_errors)
+</programlisting>
+   Helper function to increment value of a sequence, correctly handling
+   sequence options like min value, max value, increment value and cycling of
+   the sequence value. The <literal>value</> is pointer to current value which
+   should be incremented, <literal>incnum</> specifies how much should the
+   value be incremented, the rest of the options should be set to values
+   specified by the sequence's <structname>pg_sequence</structname> tuple.
+   Returns by how much was the value increased.
+  </para>
+
+  <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+                      int64 max_value, const char *valname)
+</programlisting>
+   Checks if the <literal>value</> is between <literal>min_value</>
+   and <literal>max_value</> and raises standard error message if it's not.
+   The <literal>valname</> is used as the name of the wrong value in the error
+   message.
+  </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a..223403d 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,7 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  tablesample transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc sequence \
+			  spgist tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ea0755a..bb368d2 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -888,8 +888,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
- * AM's options parser function in the case of a tuple corresponding to an
- * index, or NULL otherwise.
+ * or sequence AM's options parser function in the case of a tuple corresponding
+ * to an index or sequence respectively, or NULL otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
@@ -921,7 +921,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			options = view_reloptions(datum, false);
 			break;
 		case RELKIND_INDEX:
-			options = index_reloptions(amoptions, datum, false);
+		case RELKIND_SEQUENCE:
+			options = am_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
 			options = NULL;
@@ -1374,14 +1375,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for access methods.
  *
- *	amoptions	index AM's option parser function
+ *	amoptions	AM's option parser function
  *	reloptions	options as text[] datum
  *	validate	error flag
  */
 bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+am_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
 {
 	Assert(amoptions != NULL);
 
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..1784799
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqamapi.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqamapi.c b/src/backend/access/sequence/seqamapi.c
new file mode 100644
index 0000000..ef8e7a4
--- /dev/null
+++ b/src/backend/access/sequence/seqamapi.c
@@ -0,0 +1,191 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/seqamapi.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amstate.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_am.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * GetSeqAmRoutine - call the specified access method handler routine
+ * to get its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutine(Oid amhandler)
+{
+	Datum			datum;
+	SeqAmRoutine   *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SeqAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SeqAmRoutine))
+		elog(ERROR, "sequence access method handler function %u did not return an SeqAmRoutine struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * GetSeqAmRoutineByAMId - look up the handler of the sequence access method
+ * for the given OID, and retrieve its SeqAmRoutine struct.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineByAMId(Oid amoid)
+{
+	HeapTuple		tuple;
+	regproc			amhandler;
+	Form_pg_am		amform;
+
+	/* 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);
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("sequence access method \"%s\" does not have a handler",
+						NameStr(amform->amname))));
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function. */
+	return GetSeqAmRoutine(amhandler);
+}
+
+/*
+ * GetSeqAmRoutineForRelation - look up the handler of the sequence access
+ * method for the given sequence Relation.
+ */
+SeqAmRoutine *
+GetSeqAmRoutineForRelation(Relation relation)
+{
+	SeqAmRoutine *seqam;
+	SeqAmRoutine *cseqam;
+
+	if (relation->rd_seqamroutine == NULL)
+	{
+		if (!OidIsValid(relation->rd_rel->relam))
+		{
+			char *relname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(relation)),
+													   RelationGetRelationName(relation));
+			elog(ERROR, "relation %s isn't sequence", relname);
+		}
+
+		seqam = GetSeqAmRoutineByAMId(relation->rd_rel->relam);
+		/* Save the data for later reuse in CacheMemoryContext */
+		cseqam = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												   sizeof(SeqAmRoutine));
+		memcpy(cseqam, seqam, sizeof(SeqAmRoutine));
+		relation->rd_seqamroutine = cseqam;
+	}
+
+	return relation->rd_seqamroutine;
+}
+
+/*
+ * ValidateSeqAmRoutine - to sanity check for the SeqAmRoutine returned by a
+ * handler.
+ *
+ * At the moment we only check that the StateTypeOid is valid type oid and that
+ * the type it points to is fixed width and will fit into the sequence page.
+ */
+void
+ValidateSeqAmRoutine(SeqAmRoutine *routine)
+{
+#define MAX_SEQAM_STATE_LEN \
+	MaxHeapTupleSize - MinHeapTupleSize - MAXALIGN(sizeof(FormData_pg_sequence))
+
+	Oid	stattypeoid = routine->StateTypeOid;
+
+	if (!get_typisdefined(stattypeoid))
+		ereport(ERROR,
+				(errmsg("type with OID %u does not exist",
+						stattypeoid)));
+
+	if (TypeIsToastable(stattypeoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" must not be toastable",
+						format_type_be(stattypeoid))));
+
+	if (get_typlen(stattypeoid) >= MAX_SEQAM_STATE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("sequence access method state data type \"%s\" exceeds maximum allowed length (%lu)",
+						format_type_be(stattypeoid), MAX_SEQAM_STATE_LEN)));
+
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..03ac01f
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,414 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *	  Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+#include "utils/typcache.h"
+
+typedef struct LocalSequence
+{
+	FormData_pg_sequence	seq;
+	LocalSequenceState		state;
+} LocalSequence;
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS	32
+
+static bytea *seqam_local_reloptions(Datum reloptions, bool validate);
+static Datum seqam_local_init(Relation seqrel, Form_pg_sequence seq,
+							  int64 restart_value, bool restart_requested,
+							  bool is_init);
+static int64 seqam_local_alloc(Relation seqrel, SequenceHandle *seqh,
+							   int64 nrequested, int64 *last);
+static void seqam_local_setval(Relation seqrel, SequenceHandle *seqh,
+							   int64 new_value);
+static Datum seqam_local_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+								  Datum amstate);
+
+static char *
+seqam_local_state_get_part(StringInfo buf, char *ptr, bool *found)
+{
+	*found = false;
+
+	resetStringInfo(buf);
+
+	while (*ptr != ',' && *ptr != ')')
+	{
+		char		ch = *ptr++;
+
+		if (ch == '\0')
+		{
+			*found = false;
+			return ptr;
+		}
+
+		appendStringInfoChar(buf, ch);
+
+		if (!*found)
+			*found = true;
+	}
+
+	return ptr;
+}
+
+Datum
+seqam_local_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr;
+	Datum		d;
+	bool		found;
+	StringInfoData		buf;
+	LocalSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	initStringInfo(&buf);
+
+	/* last_value is required */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (!found)
+		goto malformed;
+	d = DirectFunctionCall1(int8in, CStringGetDatum(buf.data));
+	state->last_value = DatumGetInt64(d);
+	if (*ptr == ',')
+		ptr++;
+
+	/* is_called is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(boolin, CStringGetDatum(buf.data));
+		state->is_called = DatumGetBool(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->is_called = true;
+
+	/* log_cnt is optional */
+	ptr = seqam_local_state_get_part(&buf, ptr, &found);
+	if (found)
+	{
+		d = DirectFunctionCall1(int4in, CStringGetDatum(buf.data));
+		state->log_cnt = DatumGetInt32(d);
+		if (*ptr == ',')
+			ptr++;
+	}
+	else
+		state->log_cnt = 0;
+
+	if (*ptr != ')' || *(++ptr) != '\0')
+		goto malformed;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_local_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_local_state_out(PG_FUNCTION_ARGS)
+{
+	LocalSequenceState *state = (LocalSequenceState *) PG_GETARG_POINTER(0);
+	StringInfoData		buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("INT64_FORMAT",%s,%d)",
+					 state->last_value, state->is_called ? "t" : "f",
+					 state->log_cnt);
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the local sequence access method.
+ */
+Datum
+seqam_local_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine *seqam = makeNode(SeqAmRoutine);
+
+	seqam->StateTypeOid = SEQAMLOCALSTATEOID;
+	seqam->amoptions = seqam_local_reloptions;
+	seqam->Init = seqam_local_init;
+	seqam->Alloc = seqam_local_alloc;
+	seqam->Setval = seqam_local_setval;
+	seqam->GetState = seqam_local_get_state;
+	seqam->SetState = seqam_local_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+static bytea *
+seqam_local_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("local sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+static Datum
+seqam_local_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	LocalSequenceState	   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(LocalSequenceState));
+	else
+		seqstate = &((LocalSequence *) seq)->state;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->log_cnt = 0;
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+static int64
+seqam_local_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+				  int64 *last)
+{
+	int64		incby,
+				maxv,
+				minv,
+				log,
+				fetch,
+				result,
+				next,
+				rescnt = 0;
+	bool		is_cycled,
+				is_called,
+				logit = false;
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = &((LocalSequence *) seq)->state;
+
+	next = result = localseq->last_value;
+	incby = seq->increment_by;
+	maxv = seq->max_value;
+	minv = seq->min_value;
+	is_cycled = seq->is_cycled;
+	fetch = nrequested;
+	log = localseq->log_cnt;
+	is_called = localseq->is_called;
+
+	/* We are returning last_value if not is_called so fetch one less value. */
+	if (!is_called)
+	{
+		nrequested--;
+		fetch--;
+	}
+
+	/*
+	 * Decide whether we should emit a WAL log record.  If so, force up the
+	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+	 * cache.  (These will then be usable without logging.)
+	 *
+	 * If this is the first nextval after a checkpoint, we must force a new
+	 * WAL record to be written anyway, else replay starting from the
+	 * checkpoint would fail to advance the sequence past the logged values.
+	 * In this case we may as well fetch extra values.
+	 */
+	if (log < fetch || !is_called)
+	{
+		/* Forced log to satisfy local demand for values. */
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+	else if (sequence_needs_wal(seqh))
+	{
+		fetch = log = fetch + SEQ_LOG_VALS;
+		logit = true;
+	}
+
+	/* Fetch new result value if is_called. */
+	if (is_called)
+	{
+		rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+									 is_cycled, true);
+		result = next;
+	}
+
+	/* Fetch as many values as was requested by backend. */
+	if (rescnt < nrequested)
+		rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	/* Last value available for calling backend. */
+	*last = next;
+	/* Values we made available to calling backend can't be counted as cached. */
+	log -= rescnt;
+
+	/* We might need to fetch even more values for our own caching. */
+	if (rescnt < fetch)
+		rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+									 maxv, incby, is_cycled, false);
+
+	fetch -= rescnt;
+	log -= fetch;				/* adjust for any unfetched numbers */
+	Assert(log >= 0);
+
+	/*
+	 * Log our cached data.
+	 */
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_start_update(seqh, logit);
+
+	if (logit)
+	{
+		localseq->last_value = next;
+		localseq->log_cnt = 0;
+		localseq->is_called = true;
+
+		sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	}
+
+	localseq->last_value = *last;
+	localseq->log_cnt = log;
+	localseq->is_called = true;
+
+	sequence_save_state(seqh, PointerGetDatum(localseq), false);
+
+	sequence_finish_update(seqh);
+
+	return result;
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+static void
+seqam_local_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq;
+
+	amstate = sequence_read_state(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	localseq->last_value = new_value;		/* last fetched number */
+	localseq->is_called = true;
+	localseq->log_cnt = 0;			/* how much is logged */
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, PointerGetDatum(localseq), true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+static Datum
+seqam_local_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Datum				amstate;
+	LocalSequenceState *localseq = palloc(sizeof(LocalSequenceState));
+
+	amstate = sequence_read_state(seqh);
+	memcpy(localseq, DatumGetPointer(amstate), sizeof(LocalSequenceState));
+	sequence_release_tuple(seqh);
+
+	return PointerGetDatum(localseq);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+static void
+seqam_local_set_state(Relation seqrel, SequenceHandle *seqh,
+					  Datum amstate)
+{
+	Form_pg_sequence	seq;
+	LocalSequenceState *localseq;
+
+	seq = sequence_read_options(seqh);
+	localseq = (LocalSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(localseq->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 41d2fd4..e684f60 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -251,6 +251,7 @@ Boot_CreateStmt:
 													  false,
 													  true,
 													  false,
+													  InvalidOid,
 													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e997b57..b7cd40a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -41,6 +41,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
@@ -88,6 +89,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions);
 static ObjectAddress AddNewRelationType(const char *typeName,
@@ -849,6 +851,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					Oid relam,
 					Datum relacl,
 					Datum reloptions)
 {
@@ -922,6 +925,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
+	new_rel_reltup->relam = relam;
 
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
@@ -1035,6 +1039,7 @@ heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress)
 {
 	Relation	pg_class_desc;
@@ -1263,6 +1268,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relam,
 						PointerGetDatum(relacl),
 						reloptions);
 
@@ -1275,7 +1281,8 @@ heap_create_with_catalog(const char *relname,
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
 	 * namespace is.  Also make a dependency link to its owner, as well as
-	 * dependencies for any roles mentioned in the default ACL.
+	 * dependencies for any roles mentioned in the default ACL. When relam
+	 * is specified, record dependency on the pg_am record.
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
@@ -1330,6 +1337,14 @@ heap_create_with_catalog(const char *relname,
 								  0, NULL,
 								  nnewmembers, newmembers);
 		}
+
+		if (relkind == RELKIND_SEQUENCE)
+		{
+			referenced.classId = AccessMethodRelationId;
+			referenced.objectId = relam;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
 	}
 
 	/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index cb3ba85..b40f0c7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index f40a005..40b5e91 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -291,6 +291,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   false,
 										   true,
 										   true,
+										   InvalidOid,
 										   NULL);
 	Assert(toast_relid != InvalidOid);
 
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 904dc1c..1f2e0c4 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -15,6 +15,8 @@
 
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_am.h"
@@ -29,7 +31,7 @@
 #include "utils/syscache.h"
 
 
-static Oid	lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid	lookup_am_handler_func(List *handler_name, char amtype);
 static const char *get_am_type_string(char amtype);
 
 
@@ -72,7 +74,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	/*
 	 * Get the handler function oid, verifying the AM type while at it.
 	 */
-	amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+	amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
 
 	/*
 	 * Insert tuple into pg_am.
@@ -187,6 +189,16 @@ get_index_am_oid(const char *amname, bool missing_ok)
 }
 
 /*
+ * get_seq_am_oid - given an access method name, look up its OID
+ *		and verify it corresponds to an sequence AM.
+ */
+Oid
+get_seq_am_oid(const char *amname, bool missing_ok)
+{
+	return get_am_type_oid(amname, AMTYPE_SEQUENCE, missing_ok);
+}
+
+/*
  * get_am_oid - given an access method name, look up its OID.
  * 		The type is not checked.
  */
@@ -226,6 +238,8 @@ get_am_type_string(char amtype)
 	{
 		case AMTYPE_INDEX:
 			return "INDEX";
+		case AMTYPE_SEQUENCE:
+			return "SEQUENCE";
 		default:
 			/* shouldn't happen */
 			elog(ERROR, "invalid access method type '%c'", amtype);
@@ -240,9 +254,10 @@ get_am_type_string(char amtype)
  * This function either return valid function Oid or throw an error.
  */
 static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
 {
 	Oid			handlerOid;
+	Oid			handler_type;
 	static const Oid funcargtypes[1] = {INTERNALOID};
 
 	if (handler_name == NIL)
@@ -257,16 +272,21 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
 	switch (amtype)
 	{
 		case AMTYPE_INDEX:
-			if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
-				ereport(ERROR,
-						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-						 errmsg("function %s must return type %s",
-								NameListToString(handler_name),
-								"index_am_handler")));
+			handler_type = INDEX_AM_HANDLEROID;
+			break;
+		case AMTYPE_SEQUENCE:
+			handler_type = SEQ_AM_HANDLEROID;
 			break;
 		default:
 			elog(ERROR, "unrecognized access method type \"%c\"", amtype);
 	}
 
+	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;
 }
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5cb28cf..54a22da 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -680,6 +680,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 										  false,
 										  true,
 										  true,
+										  InvalidOid,
 										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cb7a145..ab7b7a3 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	/*
 	 * Actually create the target table
 	 */
-	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+	intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+									  NULL);
 
 	/*
 	 * If necessary, create a TOAST table for the target table.  Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 13b04e6..92ae4db 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -536,7 +536,7 @@ DefineIndex(Oid relationId,
 	reloptions = transformRelOptions((Datum) 0, stmt->options,
 									 NULL, NULL, false, false);
 
-	(void) index_reloptions(amoptions, reloptions, true);
+	(void) am_reloptions(amoptions, reloptions, true);
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..f7edcee 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,16 +14,22 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
+#include "access/tupmacs.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
@@ -36,19 +42,14 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
 /*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS	32
-
-/*
  * The "special area" of a sequence's buffer page looks like this.
  */
 #define SEQ_MAGIC	  0x1717
@@ -81,24 +82,113 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+	SeqTable	elm;
+	Relation	rel;
+	Buffer		buf;
+	Oid			statetyp;
+	int16		statetyplen;
+	bool		statetypbyval;
+	char		statetypalign;
+	bool		inupdate;
+	HeapTupleData tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
  */
 static SeqTableData *last_used_seq = NULL;
 
+static HeapTuple build_seq_tuple(Relation rel, SeqAmRoutine *seqam,
+								 Form_pg_sequence new, int64 restart_value);
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-			   Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
 			Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+							   Buffer buf, Page page);
+static HeapTuple sequence_read_tuple(SequenceHandle *seqh);
 
+/*
+ * Build template column definition for a sequence relation.
+*/
+static ColumnDef *
+makeSeqColumnDef(void)
+{
+	ColumnDef  *coldef = makeNode(ColumnDef);
+
+	coldef->inhcount = 0;
+	coldef->is_local = true;
+	coldef->is_not_null = true;
+	coldef->is_from_type = false;
+	/* Force plain storage. */
+	coldef->storage = 'p';
+	coldef->raw_default = NULL;
+	coldef->cooked_default = NULL;
+	coldef->collClause = NULL;
+	coldef->collOid = InvalidOid;
+	coldef->constraints = NIL;
+	coldef->location = -1;
+
+	return coldef;
+}
+
+/*
+ * Add additional sequence AM columns to the sequence column definition list.
+ */
+static List *
+BuildSeqColumnDefList(Oid amstateTypeOid)
+{
+	List	   *seqcols;
+	int			colid;
+
+	seqcols = NIL;
+	for (colid = SEQ_COL_FIRSTCOL; colid <= SEQ_COL_LASTCOL; colid++)
+	{
+		ColumnDef  *coldef = makeSeqColumnDef();
+
+		switch (colid)
+		{
+			case SEQ_COL_STARTVAL:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				break;
+			case SEQ_COL_INCBY:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "increment_by";
+				break;
+			case SEQ_COL_MAXVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "max_value";
+				break;
+			case SEQ_COL_MINVALUE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "min_value";
+				break;
+			case SEQ_COL_CACHE:
+				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "cache_value";
+				break;
+			case SEQ_COL_CYCLE:
+				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+				coldef->colname = "is_cycled";
+				break;
+			case SEQ_COL_AMSTATE:
+				coldef->typeName = makeTypeNameFromOid(amstateTypeOid, -1);
+				coldef->colname = "amstate";
+				break;
+		}
+
+		seqcols = lappend(seqcols, coldef);
+	}
+
+	return seqcols;
+}
 
 /*
  * DefineSequence
@@ -111,14 +201,12 @@ DefineSequence(CreateSeqStmt *seq)
 	List	   *owned_by;
 	CreateStmt *stmt = makeNode(CreateStmt);
 	Oid			seqoid;
+	Oid			seqamid;
 	ObjectAddress address;
 	Relation	rel;
 	HeapTuple	tuple;
-	TupleDesc	tupDesc;
-	Datum		value[SEQ_COL_LASTCOL];
-	bool		null[SEQ_COL_LASTCOL];
-	int			i;
-	NameData	name;
+	List	   *seqcols;
+	SeqAmRoutine *seqam;
 
 	/* Unlogged sequences are not implemented -- not clear if useful. */
 	if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -147,102 +235,33 @@ DefineSequence(CreateSeqStmt *seq)
 	/* Check and set all option values */
 	init_params(seq->options, true, &new, &owned_by);
 
-	/*
-	 * Create relation (and fill value[] and null[] for the tuple)
-	 */
-	stmt->tableElts = NIL;
-	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
-	{
-		ColumnDef  *coldef = makeNode(ColumnDef);
-
-		coldef->inhcount = 0;
-		coldef->is_local = true;
-		coldef->is_not_null = true;
-		coldef->is_from_type = false;
-		coldef->storage = 0;
-		coldef->raw_default = NULL;
-		coldef->cooked_default = NULL;
-		coldef->collClause = NULL;
-		coldef->collOid = InvalidOid;
-		coldef->constraints = NIL;
-		coldef->location = -1;
-
-		null[i - 1] = false;
-
-		switch (i)
-		{
-			case SEQ_COL_NAME:
-				coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
-				coldef->colname = "sequence_name";
-				namestrcpy(&name, seq->sequence->relname);
-				value[i - 1] = NameGetDatum(&name);
-				break;
-			case SEQ_COL_LASTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "last_value";
-				value[i - 1] = Int64GetDatumFast(new.last_value);
-				break;
-			case SEQ_COL_STARTVAL:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "start_value";
-				value[i - 1] = Int64GetDatumFast(new.start_value);
-				break;
-			case SEQ_COL_INCBY:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "increment_by";
-				value[i - 1] = Int64GetDatumFast(new.increment_by);
-				break;
-			case SEQ_COL_MAXVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "max_value";
-				value[i - 1] = Int64GetDatumFast(new.max_value);
-				break;
-			case SEQ_COL_MINVALUE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "min_value";
-				value[i - 1] = Int64GetDatumFast(new.min_value);
-				break;
-			case SEQ_COL_CACHE:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "cache_value";
-				value[i - 1] = Int64GetDatumFast(new.cache_value);
-				break;
-			case SEQ_COL_LOG:
-				coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-				coldef->colname = "log_cnt";
-				value[i - 1] = Int64GetDatum((int64) 0);
-				break;
-			case SEQ_COL_CYCLE:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_cycled";
-				value[i - 1] = BoolGetDatum(new.is_cycled);
-				break;
-			case SEQ_COL_CALLED:
-				coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
-				coldef->colname = "is_called";
-				value[i - 1] = BoolGetDatum(false);
-				break;
-		}
-		stmt->tableElts = lappend(stmt->tableElts, coldef);
-	}
+	if (seq->accessMethod)
+		seqamid = get_seq_am_oid(seq->accessMethod, false);
+	else
+		seqamid = LOCAL_SEQAM_OID;
+
+	seqam = GetSeqAmRoutineByAMId(seqamid);
+	/* Build column definitions. */
+	seqcols = BuildSeqColumnDefList(seqam->StateTypeOid);
 
 	stmt->relation = seq->sequence;
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
-	stmt->options = NIL;
+	stmt->options = seq->amoptions;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
+	stmt->tableElts = seqcols;
 
-	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+	address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+							 NULL);
 	seqoid = address.objectId;
 	Assert(seqoid != InvalidOid);
 
 	rel = heap_open(seqoid, AccessExclusiveLock);
-	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
+	/* Build new sequence tuple and store it. */
+	tuple = build_seq_tuple(rel, seqam, &new, new.start_value);
 	fill_seq_with_data(rel, tuple);
 
 	/* process OWNED BY if given */
@@ -254,6 +273,7 @@ DefineSequence(CreateSeqStmt *seq)
 	return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +287,89 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-	Relation	seq_rel;
-	SeqTable	elm;
-	Form_pg_sequence seq;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	HeapTuple	tuple;
+	Relation	seqrel;
+	SequenceHandle		seqh;
+	Form_pg_sequence	seq;
+	TupleDesc			tupDesc;
+	Datum	   values[SEQ_COL_LASTCOL];
+	bool	   nulls[SEQ_COL_LASTCOL];
+	SeqAmRoutine *seqam;
 
 	/*
-	 * Read the old sequence.  This does a bit more work than really
-	 * necessary, but it's simple, and we do want to double-check that it's
-	 * indeed a sequence.
+	 * Read and lock the old page.
 	 */
-	init_sequence(seq_relid, &elm, &seq_rel);
-	(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+	sequence_open(seqrelid, &seqh);
+	tuple = sequence_read_tuple(&seqh);
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	/*
 	 * Copy the existing sequence tuple.
 	 */
-	tuple = heap_copytuple(&seqtuple);
+	tuple = heap_copytuple(tuple);
 
 	/* Now we're done with the old page */
-	UnlockReleaseBuffer(buf);
+	sequence_release_tuple(&seqh);
 
-	/*
-	 * Modify the copied tuple to execute the restart (compare the RESTART
-	 * action in AlterSequence)
-	 */
 	seq = (Form_pg_sequence) GETSTRUCT(tuple);
-	seq->last_value = seq->start_value;
-	seq->is_called = false;
-	seq->log_cnt = 0;
+	tupDesc = RelationGetDescr(seqrel);
+	heap_deform_tuple(tuple, tupDesc, values, nulls);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(seqrel, seq, seq->start_value,
+											  true, false);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
 
 	/*
 	 * Create a new storage file for the sequence.  We want to keep the
 	 * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
 	 * Same with relminmxid, since a sequence will never contain multixacts.
 	 */
-	RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+	RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
 							  InvalidTransactionId, InvalidMultiXactId);
 
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seqrel, tuple);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	seqh.elm->cached = seqh.elm->last;
 
-	relation_close(seq_rel, NoLock);
+	/* And we're done, close the sequence. */
+	sequence_close(&seqh);
+}
+
+/*
+ * Build sequence tuple based on the sequence form and fill in the
+ * sequence AM specific info as well.
+ */
+static HeapTuple
+build_seq_tuple(Relation rel, SeqAmRoutine *seqam, Form_pg_sequence new,
+				int64 restart_value)
+{
+	TupleDesc	tupDesc;
+	HeapTuple	tuple;
+	Datum		values[SEQ_COL_LASTCOL];
+	bool		nulls[SEQ_COL_LASTCOL];
+
+	tupDesc = RelationGetDescr(rel);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	values[SEQ_COL_STARTVAL - 1] = Int64GetDatumFast(new->start_value);
+	values[SEQ_COL_INCBY - 1] = Int64GetDatumFast(new->increment_by);
+	values[SEQ_COL_MAXVALUE - 1] = Int64GetDatumFast(new->max_value);
+	values[SEQ_COL_MINVALUE - 1] = Int64GetDatumFast(new->min_value);
+	values[SEQ_COL_CACHE - 1] = Int64GetDatumFast(new->cache_value);
+	values[SEQ_COL_CYCLE - 1] = BoolGetDatum(new->is_cycled);
+	values[SEQ_COL_AMSTATE - 1] = seqam->Init(rel, new, restart_value,
+											  false, true);
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	return tuple;
 }
 
 /*
@@ -361,7 +412,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-	/* check the comment above nextval_internal()'s equivalent call. */
+	/*
+	 * If something needs to be WAL logged, make sure that xid was acquired,
+	 * so this transaction's commit will trigger a WAL flush and wait for
+	 * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+	 * no need to assign xids subxacts, that'll already trigger a appropriate
+	 * wait. (Has to be done outside of critical section).
+	 */
 	if (RelationNeedsWAL(rel))
 		GetTopTransactionId();
 
@@ -375,23 +432,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 		elog(ERROR, "failed to add sequence tuple to page");
 
 	/* XLOG stuff */
-	if (RelationNeedsWAL(rel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = rel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
+	log_sequence_tuple(rel, tuple, buf, page);
 
 	END_CRIT_SECTION();
 
@@ -399,6 +440,69 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 }
 
 /*
+ * Replace the type of amstate column.
+ *
+ * We don't do AlterTable here as that produces dead columns which we don't
+ * want. This is safe because the sequence page is controlled by code in this
+ * module and isn't changed the same way as a table.
+ *
+ * TODO: check if anybody is depending on the row-type associated with the
+ * sequence.
+ */
+static void
+replace_sequence_amstate_col(Oid seqrelid, Oid typid)
+{
+	Relation	attr_rel;
+	Datum		values[Natts_pg_attribute];
+	bool		nulls[Natts_pg_attribute];
+	bool		replace[Natts_pg_attribute];
+	HeapTuple	tp,
+				attr_tuple,
+				newattr_tuple;
+	Form_pg_type	typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+
+	memset(nulls, 0, sizeof(nulls));
+	memset(replace, 0, sizeof(replace));
+
+	replace[Anum_pg_attribute_atttypid - 1] = true;
+	replace[Anum_pg_attribute_attlen - 1] = true;
+	replace[Anum_pg_attribute_attbyval - 1] = true;
+	replace[Anum_pg_attribute_attalign - 1] = true;
+
+	values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(typid);
+	values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(typtup->typlen);
+	values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(typtup->typbyval);
+	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(typtup->typalign);
+
+	/* Build DROP command for amstate of old AM. */
+	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+	attr_tuple = SearchSysCache2(ATTNUM,
+								 ObjectIdGetDatum(seqrelid),
+								 Int16GetDatum(SEQ_COL_AMSTATE));
+	if (!HeapTupleIsValid(attr_tuple))		/* shouldn't happen */
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 SEQ_COL_AMSTATE, seqrelid);
+
+	newattr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel),
+									  values, nulls, replace);
+	simple_heap_update(attr_rel, &newattr_tuple->t_self, newattr_tuple);
+	CatalogUpdateIndexes(attr_rel, newattr_tuple);
+
+	ReleaseSysCache(tp);
+	heap_freetuple(newattr_tuple);
+	ReleaseSysCache(attr_tuple);
+
+	heap_close(attr_rel, RowExclusiveLock);
+}
+
+/*
  * AlterSequence
  *
  * Modify the definition of a sequence relation
@@ -406,19 +510,24 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-	Oid			relid;
-	SeqTable	elm;
+	Oid			seqrelid;
+	Oid			oldamid;
+	Oid			seqamid;
+	HeapTuple	tuple;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	FormData_pg_sequence new;
+	Form_pg_sequence seq,
+					 new;
 	List	   *owned_by;
 	ObjectAddress address;
+	int64		restart_value;
+	bool		restart_requested;
+	SequenceHandle seqh;
+	SeqAmRoutine *oldseqam;
 
 	/* Open and lock sequence. */
-	relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-	if (relid == InvalidOid)
+	seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+	if (seqrelid == InvalidOid)
 	{
 		ereport(NOTICE,
 				(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +535,187 @@ AlterSequence(AlterSeqStmt *stmt)
 		return InvalidObjectAddress;
 	}
 
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(seqrelid, &seqh);
+	seqrel = seqh.rel;
+	oldamid = seqrel->rd_rel->relam;
+	oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
 	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(relid, GetUserId()))
+	if (!pg_class_ownercheck(seqrelid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 					   stmt->sequence->relname);
 
 	/* lock page' buffer and read tuple into new sequence structure */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	tuple = sequence_read_tuple(&seqh);
+	seq = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
+	tuple = heap_copytuple(tuple);
+	new = (Form_pg_sequence) GETSTRUCT(tuple);
 
 	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	init_params(stmt->options, false, new, &owned_by);
 
-	/* Clear local cache so that we don't think we have cached numbers */
-	/* Note that we do not change the currval() state */
-	elm->cached = elm->last;
+	if (stmt->accessMethod)
+		seqamid = get_seq_am_oid(stmt->accessMethod, false);
+	else
+		seqamid = oldamid;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
+	restart_value = sequence_get_restart_value(stmt->options, new->start_value,
+											   &restart_requested);
 
-	/* Now okay to update the on-disk tuple */
-	START_CRIT_SECTION();
+	/*
+	 * If we are changing sequence AM, we need to alter the sequence relation.
+	 */
+	if (seqamid != oldamid)
+	{
+		ObjectAddress	myself,
+						referenced;
+		Relation        pgcrel;
+		HeapTuple       pgctup,
+						newpgctuple;
+		HeapTuple       seqamtup;
+		Form_pg_am		form_am;
+		Datum			reloptions;
+		Datum			values[Natts_pg_class];
+		bool			nulls[Natts_pg_class];
+		bool			replace[Natts_pg_class];
+		static char	   *validnsps[2];
+		SeqAmRoutine	   *newseqam;
+
+		oldseqam = GetSeqAmRoutineByAMId(oldamid);
 
-	memcpy(seq, &new, sizeof(FormData_pg_sequence));
+		/*
+		 * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+		 * statement, we use nextval of the old sequence AM to provide
+		 * restart point for the new sequence AM.
+		 */
+		if (!restart_requested)
+		{
+			int64 last;
+			restart_value = oldseqam->Alloc(seqrel, &seqh, 1, &last);
+		}
 
-	MarkBufferDirty(buf);
+		sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
 
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
+		/* We don't need the old sequence tuple anymore. */
+		sequence_release_tuple(&seqh);
+
+		/* Parse the new reloptions. */
+		seqamtup = SearchSysCache1(AMOID, ObjectIdGetDatum(seqamid));
+		if (!HeapTupleIsValid(seqamtup))
+			elog(ERROR, "cache lookup failed for sequence access method %u",
+				 seqamid);
+
+		newseqam = GetSeqAmRoutineByAMId(seqamid);
+
+		form_am = (Form_pg_am) GETSTRUCT(seqamtup);
+
+		validnsps[0] = NameStr(form_am->amname);
+		validnsps[1] = NULL;
+
+		reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+										 validnsps, true, false);
 
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+		(void) am_reloptions(newseqam->amoptions, reloptions, true);
+		ReleaseSysCache(seqamtup);
 
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+		/* Update the pg_class entry. */
+		pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+		pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+		if (!HeapTupleIsValid(pgctup))
+			elog(ERROR, "pg_class entry for sequence %u unavailable",
+				 seqrelid);
 
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+		memset(values, 0, sizeof(values));
+		memset(nulls, false, sizeof(nulls));
+		memset(replace, false, sizeof(replace));
 
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+		values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+		replace[Anum_pg_class_relam - 1] = true;
 
-		PageSetLSN(page, recptr);
+		if (reloptions != (Datum) 0)
+			values[Anum_pg_class_reloptions - 1] = reloptions;
+		else
+			nulls[Anum_pg_class_reloptions - 1] = true;
+		replace[Anum_pg_class_reloptions - 1] = true;
+
+		newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+										values, nulls, replace);
+
+		simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+		CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+		heap_freetuple(newpgctuple);
+		ReleaseSysCache(pgctup);
+
+		heap_close(pgcrel, NoLock);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Create a new storage file for the sequence.
+		 * And change the type definition.
+		 *
+		 * We can't use AlterTable internals here because the sequence
+		 * has to have the expected number of columns and no
+		 * attisdropped = true columns.
+		 */
+		RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+								  InvalidTransactionId, InvalidMultiXactId);
+		replace_sequence_amstate_col(seqrelid, newseqam->StateTypeOid);
+		CommandCounterIncrement();
+
+		/* Rebuild the sequence tuple and save it. */
+		tuple = build_seq_tuple(seqrel, newseqam, new, restart_value);
+		fill_seq_with_data(seqh.rel, tuple);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+										AccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqrelid;
+		myself.objectSubId = 0;
+		referenced.classId = AccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
+	else
+	{
+		Datum	   newamstate;
 
-	END_CRIT_SECTION();
+		sequence_check_range(restart_value, new->min_value, new->max_value,
+							 restart_requested ? "RESTART" : "START");
 
-	UnlockReleaseBuffer(buf);
+		/* Let the new sequence AM initialize. */
+		newamstate = oldseqam->Init(seqrel, new, restart_value,
+									restart_requested, false);
+
+		sequence_start_update(&seqh, true);
+		memcpy(seq, new, sizeof(FormData_pg_sequence));
+		sequence_save_state(&seqh, newamstate, true);
+		sequence_finish_update(&seqh);
+		sequence_release_tuple(&seqh);
+	}
+
+	/* Clear local cache so that we don't think we have cached numbers */
+	/* Note that we do not change the currval() state */
+	seqh.elm->cached = seqh.elm->last;
 
 	/* process OWNED BY if given */
 	if (owned_by)
 		process_owned_by(seqrel, owned_by);
 
-	InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+	InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-	ObjectAddressSet(address, RelationRelationId, relid);
+	ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	return address;
 }
@@ -530,29 +756,26 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	Page		page;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
-	int64		incby,
-				maxv,
-				minv,
-				cache,
-				log,
-				fetch,
-				last;
-	int64		result,
-				next,
-				rescnt = 0;
-	bool		logit = false;
+	Form_pg_sequence seq_form;
+	int64		last,
+				result;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
 	if (pg_class_aclcheck(elm->relid, GetUserId(),
 						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -577,121 +800,15 @@ nextval_internal(Oid relid)
 		Assert(elm->last_valid);
 		Assert(elm->increment != 0);
 		elm->last += elm->increment;
-		relation_close(seqrel, NoLock);
+		sequence_close(&seqh);
 		last_used_seq = elm;
 		return elm->last;
 	}
 
 	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-	page = BufferGetPage(buf);
-
-	last = next = result = seq->last_value;
-	incby = seq->increment_by;
-	maxv = seq->max_value;
-	minv = seq->min_value;
-	fetch = cache = seq->cache_value;
-	log = seq->log_cnt;
-
-	if (!seq->is_called)
-	{
-		rescnt++;				/* return last_value if not is_called */
-		fetch--;
-	}
+	seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-	/*
-	 * Decide whether we should emit a WAL log record.  If so, force up the
-	 * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-	 * cache.  (These will then be usable without logging.)
-	 *
-	 * If this is the first nextval after a checkpoint, we must force a new
-	 * WAL record to be written anyway, else replay starting from the
-	 * checkpoint would fail to advance the sequence past the logged values.
-	 * In this case we may as well fetch extra values.
-	 */
-	if (log < fetch || !seq->is_called)
-	{
-		/* forced log to satisfy local demand for values */
-		fetch = log = fetch + SEQ_LOG_VALS;
-		logit = true;
-	}
-	else
-	{
-		XLogRecPtr	redoptr = GetRedoRecPtr();
-
-		if (PageGetLSN(page) <= redoptr)
-		{
-			/* last update of seq was before checkpoint */
-			fetch = log = fetch + SEQ_LOG_VALS;
-			logit = true;
-		}
-	}
-
-	while (fetch)				/* try to fetch cache [+ log ] numbers */
-	{
-		/*
-		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
-		 * sequences
-		 */
-		if (incby > 0)
-		{
-			/* ascending sequence */
-			if ((maxv >= 0 && next > maxv - incby) ||
-				(maxv < 0 && next + incby > maxv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = minv;
-			}
-			else
-				next += incby;
-		}
-		else
-		{
-			/* descending sequence */
-			if ((minv < 0 && next < minv - incby) ||
-				(minv >= 0 && next + incby < minv))
-			{
-				if (rescnt > 0)
-					break;		/* stop fetching */
-				if (!seq->is_cycled)
-				{
-					char		buf[100];
-
-					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-					ereport(ERROR,
-						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-								  RelationGetRelationName(seqrel), buf)));
-				}
-				next = maxv;
-			}
-			else
-				next += incby;
-		}
-		fetch--;
-		if (rescnt < cache)
-		{
-			log--;
-			rescnt++;
-			last = next;
-			if (rescnt == 1)	/* if it's first result - */
-				result = next;	/* it's what to return */
-		}
-	}
-
-	log -= fetch;				/* adjust for any unfetched numbers */
-	Assert(log >= 0);
+	result = seqam->Alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
 	/* save info in local cache */
 	elm->last = result;			/* last returned number */
@@ -700,70 +817,8 @@ nextval_internal(Oid relid)
 
 	last_used_seq = elm;
 
-	/*
-	 * If something needs to be WAL logged, acquire an xid, so this
-	 * transaction's commit will trigger a WAL flush and wait for syncrep.
-	 * It's sufficient to ensure the toplevel transaction has an xid, no need
-	 * to assign xids subxacts, that'll already trigger an appropriate wait.
-	 * (Have to do that here, so we're outside the critical section)
-	 */
-	if (logit && RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	/*
-	 * We must mark the buffer dirty before doing XLogInsert(); see notes in
-	 * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-	 * This looks like a violation of the buffer update protocol, but it is in
-	 * fact safe because we hold exclusive lock on the buffer.  Any other
-	 * process, including a checkpoint, that tries to examine the buffer
-	 * contents will block until we release the lock, and then will see the
-	 * final state that we install below.
-	 */
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (logit && RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-
-		/*
-		 * We don't log the current state of the tuple, but rather the state
-		 * as it would appear after "log" more fetches.  This lets us skip
-		 * that many future WAL records, at the cost that we lose those
-		 * sequence values if we crash.
-		 */
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		/* set values that will be saved in xlog */
-		seq->last_value = next;
-		seq->is_called = true;
-		seq->log_cnt = 0;
-
-		xlrec.node = seqrel->rd_node;
-
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	/* Now update sequence tuple to the intended final state */
-	seq->last_value = last;		/* last fetched number */
-	seq->is_called = true;
-	seq->log_cnt = log;			/* how much is logged */
-
-	END_CRIT_SECTION();
-
-	UnlockReleaseBuffer(buf);
-
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return result;
 }
@@ -773,28 +828,27 @@ currval_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
-	SeqTable	elm;
-	Relation	seqrel;
+	SequenceHandle seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(),
+	if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
 						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	if (!elm->last_valid)
+	if (!seqh.elm->last_valid)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
-	result = elm->last;
+	result = seqh.elm->last;
 
-	relation_close(seqrel, NoLock);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(result);
 }
@@ -835,31 +889,26 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+	Oid			relid = PG_GETARG_OID(0);
+	int64		next = PG_GETARG_INT64(1);
 	SeqTable	elm;
 	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
-	Form_pg_sequence seq;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
@@ -876,106 +925,86 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	 */
 	PreventCommandIfParallelMode("setval()");
 
-	/* lock page' buffer and read tuple */
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-	if ((next < seq->min_value) || (next > seq->max_value))
-	{
-		char		bufv[100],
-					bufm[100],
-					bufx[100];
-
-		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-						bufv, RelationGetRelationName(seqrel),
-						bufm, bufx)));
-	}
+	seqam->Setval(seqrel, &seqh, next);
 
-	/* Set the currval() state only if iscalled = true */
-	if (iscalled)
-	{
-		elm->last = next;		/* last returned number */
-		elm->last_valid = true;
-	}
-
-	/* In any case, forget any future cached numbers */
+	/* Reset local cached data */
+	elm->last = next;		/* last returned number */
+	elm->last_valid = true;
 	elm->cached = elm->last;
 
-	/* check the comment above nextval_internal()'s equivalent call. */
-	if (RelationNeedsWAL(seqrel))
-		GetTopTransactionId();
-
-	/* ready to change the on-disk (or really, in-buffer) tuple */
-	START_CRIT_SECTION();
-
-	seq->last_value = next;		/* last fetched number */
-	seq->is_called = iscalled;
-	seq->log_cnt = 0;
-
-	MarkBufferDirty(buf);
-
-	/* XLOG stuff */
-	if (RelationNeedsWAL(seqrel))
-	{
-		xl_seq_rec	xlrec;
-		XLogRecPtr	recptr;
-		Page		page = BufferGetPage(buf);
-
-		XLogBeginInsert();
-		XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-		xlrec.node = seqrel->rd_node;
-		XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-		XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-		PageSetLSN(page, recptr);
-	}
-
-	END_CRIT_SECTION();
+	last_used_seq = elm;
 
-	UnlockReleaseBuffer(buf);
+	sequence_close(&seqh);
 
-	relation_close(seqrel, NoLock);
+	PG_RETURN_INT64(next);
 }
 
 /*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
+ * Implement the 3 arg setval procedure.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
-setval_oid(PG_FUNCTION_ARGS)
+setval3_oid(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		next = PG_GETARG_INT64(1);
+	bool		iscalled = PG_GETARG_BOOL(2);
+	LocalSequenceState	state;
+	SeqTable	elm;
+	Relation	seqrel;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
 
-	do_setval(relid, next, true);
+	/* open and AccessShareLock sequence */
+	sequence_open(relid, &seqh);
+	elm = seqh.elm;
+	seqrel = seqh.rel;
+	seqam = GetSeqAmRoutineForRelation(seqrel);
 
-	PG_RETURN_INT64(next);
-}
+	if (pg_class_aclcheck(elm->relid, GetUserId(),
+						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	/* read-only transactions may only modify temp sequences */
+	if (!seqrel->rd_islocaltemp)
+		PreventCommandIfReadOnly("setval()");
+
+	/* Make sure the target sequence is 'local' sequence. */
+	if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+	/* Build the state and pass it to sequence AM. */
+	state.last_value = next;
+	state.log_cnt = 0;
+	state.is_called = iscalled;
+	seqam->SetState(seqh.rel, &seqh, PointerGetDatum(&state));
+
+	/* Set the currval() state only if iscalled = true */
+	if (iscalled)
+	{
+		elm->last = next;		/* last returned number */
+		elm->last_valid = true;
+	}
 
-/*
- * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval3_oid(PG_FUNCTION_ARGS)
-{
-	Oid			relid = PG_GETARG_OID(0);
-	int64		next = PG_GETARG_INT64(1);
-	bool		iscalled = PG_GETARG_BOOL(2);
+	/* Reset local cached data */
+	elm->cached = elm->last;
+
+	last_used_seq = elm;
 
-	do_setval(relid, next, iscalled);
+	sequence_close(&seqh);
 
 	PG_RETURN_INT64(next);
 }
 
-
 /*
  * Open the sequence and acquire AccessShareLock if needed
  *
@@ -1034,11 +1063,10 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
 {
 	SeqTable	elm;
 	Relation	seqrel;
@@ -1090,44 +1118,59 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	}
 
 	/* Return results */
-	*p_elm = elm;
-	*p_rel = seqrel;
+	seqh->elm = elm;
+	seqh->rel = seqrel;
+	seqh->buf = InvalidBuffer;
+	seqh->tup.t_data = NULL;
+	seqh->tup.t_len = 0;
+	seqh->statetyp = InvalidOid;
+	seqh->statetyplen = -1;
+	seqh->statetypbyval = false;
+	seqh->statetypalign = '\0';
+	seqh->inupdate = false;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+	Assert(!seqh->inupdate);
+
+	relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *		(this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
 	Page		page;
+	Buffer		buf;
 	ItemId		lp;
 	sequence_magic *sm;
-	Form_pg_sequence seq;
 
-	*buf = ReadBuffer(rel, 0);
-	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+	if (seqh->tup.t_data != NULL)
+		return &seqh->tup;
+
+	seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-	page = BufferGetPage(*buf);
+	page = BufferGetPage(buf);
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
 		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-			 RelationGetRelationName(rel), sm->magic);
+			 RelationGetRelationName(seqh->rel), sm->magic);
 
 	lp = PageGetItemId(page, FirstOffsetNumber);
 	Assert(ItemIdIsNormal(lp));
 
-	/* Note we currently only bother to set these two fields of *seqtuple */
-	seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	seqtuple->t_len = ItemIdGetLength(lp);
+	/* Note we currently only bother to set these two fields of the tuple */
+	seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	seqh->tup.t_len = ItemIdGetLength(lp);
 
 	/*
 	 * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1137,33 +1180,181 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
-	Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-	if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+	Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+	if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+	{
+		HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+		seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+		seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+		MarkBufferDirtyHint(buf, true);
+	}
+
+	/* update our copy of the increment if needed */
+	if (seqh->elm->increment == 0)
+	{
+		Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+		seqh->elm->increment = seq->increment_by;
+	}
+
+	return &seqh->tup;
+}
+
+Form_pg_sequence
+sequence_read_options(SequenceHandle *seqh)
+{
+	return (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(seqh));
+}
+
+Datum
+sequence_read_state(SequenceHandle *seqh)
+{
+	HeapTuple		tuple = sequence_read_tuple(seqh);
+	bool			isnull;
+	TupleDesc		tupDesc;
+	Datum			res;
+
+	tupDesc = RelationGetDescr(seqh->rel);
+
+	res = fastgetattr(tuple, SEQ_COL_AMSTATE, tupDesc, &isnull);
+	Assert(!isnull);
+
+	return res;
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool dowal)
+{
+	Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+	if (seqh->statetyp == InvalidOid)
+	{
+		SeqAmRoutine *seqam = GetSeqAmRoutineForRelation(seqh->rel);
+		seqh->statetyp = seqam->StateTypeOid;
+
+		get_typlenbyvalalign(seqh->statetyp, &seqh->statetyplen,
+							 &seqh->statetypbyval, &seqh->statetypalign);
+		Assert(seqh->statetyplen > 0);
+	}
+
+	if (dowal)
+		GetTopTransactionId();
+
+	seqh->inupdate = true;
+
+	START_CRIT_SECTION();
+}
+
+void
+sequence_save_state(SequenceHandle *seqh, Datum amstate, bool dowal)
+{
+	char	   *seqstate = DatumGetPointer(sequence_read_state(seqh));
+	Page		page;
+
+	Assert(seqh->inupdate);
+
+
+	/*
+	 * Update the state data inline.
+	 *
+	 * This is only needed when the provided amstate datum points to different
+	 * data than what is already in the tuple.
+	 */
+	if (DatumGetPointer(amstate) != seqstate)
+	{
+		if (seqh->statetypbyval)
+			store_att_byval(seqstate, amstate, seqh->statetyplen);
+		else
+			memmove(seqstate, DatumGetPointer(amstate), seqh->statetyplen);
+	}
+
+	page = BufferGetPage(seqh->buf);
+	MarkBufferDirtyHint(seqh->buf, true);
+
+	if (dowal && RelationNeedsWAL(seqh->rel))
+		log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+	Assert(seqh->inupdate);
+
+	END_CRIT_SECTION();
+
+	seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+	/* Remove the tuple from cache */
+	if (seqh->tup.t_data != NULL)
+	{
+		seqh->tup.t_data = NULL;
+		seqh->tup.t_len = 0;
+	}
+
+	/* Release the page lock */
+	if (BufferIsValid(seqh->buf))
 	{
-		HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-		seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-		seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-		MarkBufferDirtyHint(*buf, true);
+		UnlockReleaseBuffer(seqh->buf);
+		seqh->buf = InvalidBuffer;
 	}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+	Page		page;
+	XLogRecPtr	redoptr;
 
-	seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	Assert(BufferIsValid(seqh->buf));
 
-	/* this is a handy place to update our copy of the increment */
-	elm->increment = seq->increment_by;
+	if (!RelationNeedsWAL(seqh->rel))
+		return false;
 
-	return seq;
+	page = BufferGetPage(seqh->buf);
+	redoptr = GetRedoRecPtr();
+
+	return (PageGetLSN(page) <= redoptr);
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
+ *
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
 			Form_pg_sequence new, List **owned_by)
 {
 	DefElem    *start_value = NULL;
@@ -1173,13 +1364,13 @@ init_params(List *options, bool isInit,
 	DefElem    *min_value = NULL;
 	DefElem    *cache_value = NULL;
 	DefElem    *is_cycled = NULL;
-	ListCell   *option;
+	ListCell   *param;
 
 	*owned_by = NIL;
 
-	foreach(option, options)
+	foreach(param, params)
 	{
-		DefElem    *defel = (DefElem *) lfirst(option);
+		DefElem    *defel = (DefElem *) lfirst(param);
 
 		if (strcmp(defel->defname, "increment") == 0)
 		{
@@ -1250,13 +1441,6 @@ init_params(List *options, bool isInit,
 				 defel->defname);
 	}
 
-	/*
-	 * We must reset log_cnt when isInit or when changing any parameters that
-	 * would affect future nextval allocations.
-	 */
-	if (isInit)
-		new->log_cnt = 0;
-
 	/* INCREMENT BY */
 	if (increment_by != NULL)
 	{
@@ -1265,7 +1449,6 @@ init_params(List *options, bool isInit,
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("INCREMENT must not be zero")));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->increment_by = 1;
@@ -1275,7 +1458,6 @@ init_params(List *options, bool isInit,
 	{
 		new->is_cycled = intVal(is_cycled->arg);
 		Assert(BoolIsValid(new->is_cycled));
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->is_cycled = false;
@@ -1284,7 +1466,6 @@ init_params(List *options, bool isInit,
 	if (max_value != NULL && max_value->arg)
 	{
 		new->max_value = defGetInt64(max_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || max_value != NULL)
 	{
@@ -1292,14 +1473,12 @@ init_params(List *options, bool isInit,
 			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
 		else
 			new->max_value = -1;	/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* MINVALUE (null arg means NO MINVALUE) */
 	if (min_value != NULL && min_value->arg)
 	{
 		new->min_value = defGetInt64(min_value);
-		new->log_cnt = 0;
 	}
 	else if (isInit || min_value != NULL)
 	{
@@ -1307,7 +1486,6 @@ init_params(List *options, bool isInit,
 			new->min_value = 1; /* ascending seq */
 		else
 			new->min_value = SEQ_MINVALUE;		/* descending seq */
-		new->log_cnt = 0;
 	}
 
 	/* crosscheck min/max */
@@ -1361,48 +1539,6 @@ init_params(List *options, bool isInit,
 					 bufs, bufm)));
 	}
 
-	/* RESTART [WITH] */
-	if (restart_value != NULL)
-	{
-		if (restart_value->arg != NULL)
-			new->last_value = defGetInt64(restart_value);
-		else
-			new->last_value = new->start_value;
-		new->is_called = false;
-		new->log_cnt = 0;
-	}
-	else if (isInit)
-	{
-		new->last_value = new->start_value;
-		new->is_called = false;
-	}
-
-	/* crosscheck RESTART (or current value, if changing MIN/MAX) */
-	if (new->last_value < new->min_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			   errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-					  bufs, bufm)));
-	}
-	if (new->last_value > new->max_value)
-	{
-		char		bufs[100],
-					bufm[100];
-
-		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-				   bufs, bufm)));
-	}
-
 	/* CACHE */
 	if (cache_value != NULL)
 	{
@@ -1417,7 +1553,6 @@ init_params(List *options, bool isInit,
 					 errmsg("CACHE (%s) must be greater than zero",
 							buf)));
 		}
-		new->log_cnt = 0;
 	}
 	else if (isInit)
 		new->cache_value = 1;
@@ -1528,20 +1663,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	Datum		values[5];
 	bool		isnull[5];
-	SeqTable	elm;
-	Relation	seqrel;
-	Buffer		buf;
-	HeapTupleData seqtuple;
 	Form_pg_sequence seq;
+	SequenceHandle  seqh;
 
 	/* open and AccessShareLock sequence */
-	init_sequence(relid, &elm, &seqrel);
+	sequence_open(relid, &seqh);
 
 	if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied for sequence %s",
-						RelationGetRelationName(seqrel))));
+						RelationGetRelationName(seqh.rel))));
 
 	tupdesc = CreateTemplateTupleDesc(5, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,7 +1691,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	memset(isnull, 0, sizeof(isnull));
 
-	seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+	seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
 	values[0] = Int64GetDatum(seq->start_value);
 	values[1] = Int64GetDatum(seq->min_value);
@@ -1567,12 +1699,85 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 	values[3] = Int64GetDatum(seq->increment_by);
 	values[4] = BoolGetDatum(seq->is_cycled);
 
-	UnlockReleaseBuffer(buf);
-	relation_close(seqrel, NoLock);
+	sequence_release_tuple(&seqh);
+	sequence_close(&seqh);
 
 	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	Datum		state;
+	char	   *statestr;
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typoutput;
+	bool		typisvarlena;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type output function. */
+	getTypeOutputInfo(seqam->StateTypeOid, &typoutput, &typisvarlena);
+
+	/* Get the output and convert it to string. */
+	state = seqam->GetState(seqh.rel, &seqh);
+	statestr = OidOutputFunctionCall(typoutput, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_TEXT_P(cstring_to_text(statestr));
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	char	   *statestr = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	SequenceHandle seqh;
+	SeqAmRoutine *seqam;
+	Oid			typinput,
+				typioparam;
+	Datum		state;
+
+	/* Load the sequence AM */
+	sequence_open(relid, &seqh);
+	seqam = GetSeqAmRoutineForRelation(seqh.rel);
+
+	/* Get the type input function. */
+	getTypeInputInfo(seqam->StateTypeOid, &typinput, &typioparam);
+
+	/* Convert the string to the state type and set it as new state. */
+	state = OidInputFunctionCall(typinput, statestr, typioparam, -1);
+	seqam->SetState(seqh.rel, &seqh, state);
+
+	sequence_close(&seqh);
+
+	PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+				   Buffer buf, Page page)
+{
+	xl_seq_rec	xlrec;
+	XLogRecPtr	recptr;
+
+	XLogBeginInsert();
+	XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+	xlrec.node = seqrel->rd_node;
+
+	XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+	XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+	recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+	PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1638,3 +1843,149 @@ ResetSequenceCaches(void)
 
 	last_used_seq = NULL;
 }
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+				   int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+	int64		next = *value;
+	int64		rescnt = 0;
+
+	while (incnum)
+	{
+		/*
+		 * Check MAXVALUE for ascending sequences and MINVALUE for descending
+		 * sequences
+		 */
+		if (incby > 0)
+		{
+			/* ascending sequence */
+			if ((maxv >= 0 && next > maxv - incby) ||
+				(maxv < 0 && next + incby > maxv))
+			{
+				/*
+				 * We were asked to not report errors, return without
+				 * incrementing and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = minv;
+			}
+			else
+				next += incby;
+		}
+		else
+		{
+			/* descending sequence */
+			if ((minv < 0 && next < minv - incby) ||
+				(minv >= 0 && next + incby < minv))
+			{
+				/*
+				 * We were asked to not report errors, return without incrementing
+				 * and let the caller handle it.
+				 */
+				if (!report_errors)
+					return rescnt;
+				if (!is_cycled)
+				{
+					char		buf[100];
+
+					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+					ereport(ERROR,
+						  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+								  RelationGetRelationName(seqrel), buf)));
+				}
+				next = maxv;
+			}
+			else
+				next += incby;
+		}
+		rescnt++;
+		incnum--;
+	}
+
+	*value = next;
+
+	return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+	if (value < min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+						valname, bufs, bufm)));
+	}
+
+	if (value > max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+					 valname, bufs, bufm)));
+	}
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+	ListCell *opt;
+
+	foreach(opt, options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(opt);
+
+		if (strcmp(defel->defname, "restart") == 0)
+		{
+			*found = true;
+			if (defel->arg != NULL)
+				return defGetInt64(defel);
+			else
+				return default_value;
+		}
+	}
+
+	*found = false;
+	return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 96dc923..6e6ee5d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -269,6 +270,7 @@ struct DropRelationCallbackState
 #define		ATT_INDEX				0x0008
 #define		ATT_COMPOSITE_TYPE		0x0010
 #define		ATT_FOREIGN_TABLE		0x0020
+#define		ATT_SEQUENCE			0x0040
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -454,7 +456,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * ----------------------------------------------------------------
  */
 ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
 			   ObjectAddress *typaddress)
 {
 	char		relname[NAMEDATALEN];
@@ -473,7 +475,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	Datum		reloptions;
 	ListCell   *listptr;
 	AttrNumber	attnum;
-	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
 	ObjectAddress address;
 
@@ -551,13 +552,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
-	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
-									 true, false);
+	if (relkind == RELKIND_SEQUENCE)
+	{
+		SeqAmRoutine *seqam;
+
+		Assert(relamid != InvalidOid);
+		seqam = GetSeqAmRoutineByAMId(relamid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 NULL, true, false);
 
-	if (relkind == RELKIND_VIEW)
-		(void) view_reloptions(reloptions, true);
+		(void) am_reloptions(seqam->amoptions, reloptions, true);
+	}
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+	{
+		static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+		Assert(relamid == InvalidOid);
+		reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+										 validnsps, true, false);
+		if (relkind == RELKIND_VIEW)
+			(void) view_reloptions(reloptions, true);
+		else
+			(void) heap_reloptions(relkind, reloptions, true);
+	}
 
 	if (stmt->ofTypename)
 	{
@@ -678,6 +695,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  true,
 										  allowSystemTableMods,
 										  false,
+										  relamid,
 										  typaddress);
 
 	/* Store inheritance information for new rel. */
@@ -3308,7 +3326,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* reset them all, then set just these */
-			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW |
+								ATT_INDEX | ATT_SEQUENCE);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -4308,6 +4327,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
 		case RELKIND_FOREIGN_TABLE:
 			actual_target = ATT_FOREIGN_TABLE;
 			break;
+		case RELKIND_SEQUENCE:
+			actual_target = ATT_SEQUENCE;
+			break;
 		default:
 			actual_target = 0;
 			break;
@@ -4351,8 +4373,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
 		case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
 			msg = _("\"%s\" is not a table, view, or foreign table");
 			break;
-		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
-			msg = _("\"%s\" is not a table, view, materialized view, or index");
+		case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+			msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
 			break;
 		case ATT_TABLE | ATT_MATVIEW:
 			msg = _("\"%s\" is not a table or materialized view");
@@ -9403,12 +9425,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 			(void) view_reloptions(newOptions, true);
 			break;
 		case RELKIND_INDEX:
-			(void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			(void) am_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+			break;
+		case RELKIND_SEQUENCE:
+			(void) am_reloptions(rel->rd_seqamroutine->amoptions, newOptions, true);
 			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
+					 errmsg("\"%s\" is not a table, view, materialized view, index, sequence, or TOAST table",
 							RelationGetRelationName(rel))));
 			break;
 	}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 71d4df9..ca53b8c 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2116,7 +2116,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
 	/*
 	 * Finally create the relation.  This also creates the type.
 	 */
-	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+				   &address);
 
 	return address;
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e9d9ba2..ca0f481 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
 		 * existing view, so we don't need more code to complain if "replace"
 		 * is false).
 		 */
-		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+		address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+								 InvalidOid, NULL);
 		Assert(address.objectId != InvalidOid);
 		return address;
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f4e4a91..9ea0eb8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3565,7 +3565,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(ownerId);
+	COPY_STRING_FIELD(accessMethod);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	return newnode;
@@ -3578,7 +3580,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
 	COPY_NODE_FIELD(sequence);
 	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(amoptions);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_STRING_FIELD(accessMethod);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 854c062..5972ca6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1627,7 +1627,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(ownerId);
+	COMPARE_STRING_FIELD(accessMethod);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
 	return true;
@@ -1638,7 +1640,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
 	COMPARE_NODE_FIELD(sequence);
 	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(amoptions);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_STRING_FIELD(accessMethod);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1273352..75af985 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -310,7 +310,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <list>	event_trigger_when_list event_trigger_value_list
 %type <defelt>	event_trigger_when_item
-%type <chr>		enable_trigger
+%type <chr>		enable_trigger am_type
 
 %type <str>		copy_file_name
 				database_name access_method_clause access_method attr_name
@@ -605,7 +605,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 METHOD 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
@@ -3587,7 +3588,33 @@ CreateSeqStmt:
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 					$4->relpersistence = $2;
 					n->sequence = $4;
+					n->accessMethod = NULL;
 					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = NIL;
+					n->ownerId = InvalidOid;
+					$$ = (Node *)n;
+				}
+			| CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					$4->relpersistence = $2;
+					n->sequence = $4;
+					n->accessMethod = $7;
+					n->options = $5;
+					n->amoptions = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -3609,7 +3636,31 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $3;
+					n->accessMethod = NULL;
 					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = NIL;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $3;
+					n->accessMethod = $6;
+					n->options = $4;
+					n->amoptions = $8;
 					n->missing_ok = false;
 					$$ = (Node *)n;
 				}
@@ -3617,11 +3668,34 @@ AlterSeqStmt:
 				{
 					AlterSeqStmt *n = makeNode(AlterSeqStmt);
 					n->sequence = $5;
+					n->accessMethod = NULL;
 					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = NIL;
+					n->missing_ok = true;
+					$$ = (Node *)n;
+				}
+			| ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+				USING access_method WITH reloptions
+				{
+					AlterSeqStmt *n = makeNode(AlterSeqStmt);
+					n->sequence = $5;
+					n->accessMethod = $8;
+					n->options = $6;
+					n->amoptions = $10;
 					n->missing_ok = true;
 					$$ = (Node *)n;
 				}
-
 		;
 
 OptSeqOptList: SeqOptList							{ $$ = $1; }
@@ -3680,7 +3754,7 @@ SeqOptElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
 				}
-		;
+			;
 
 opt_by:		BY				{}
 			| /* empty */	{}
@@ -4715,16 +4789,21 @@ row_security_cmd:
  *
  *****************************************************************************/
 
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
 				{
 					CreateAmStmt *n = makeNode(CreateAmStmt);
 					n->amname = $4;
 					n->handler_name = $8;
-					n->amtype = AMTYPE_INDEX;
+					n->amtype = $6;
 					$$ = (Node *) n;
 				}
 		;
 
+am_type:
+			INDEX		{ $$ = AMTYPE_INDEX; }
+		|	SEQUENCE	{ $$ = AMTYPE_SEQUENCE; }
+		;
+
 /*****************************************************************************
  *
  *		QUERIES :
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6528494..509fa32 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -29,6 +29,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -456,6 +457,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		seqstmt = makeNode(CreateSeqStmt);
 		seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 		seqstmt->options = NIL;
+		seqstmt->amoptions = NIL;
+		seqstmt->accessMethod = NULL;
 
 		/*
 		 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4d0aac9..8b95577 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -965,7 +965,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_RELATION,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							EventTriggerCollectSimpleCommand(address,
 															 secondaryObject,
 															 stmt);
@@ -998,7 +999,8 @@ ProcessUtilitySlow(Node *parsetree,
 							/* Create the table itself */
 							address = DefineRelation((CreateStmt *) stmt,
 													 RELKIND_FOREIGN_TABLE,
-													 InvalidOid, NULL);
+													 InvalidOid, InvalidOid,
+													 NULL);
 							CreateForeignTable((CreateForeignTableStmt *) stmt,
 											   address.objectId);
 							EventTriggerCollectSimpleCommand(address,
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index dd447cf..6c1456e 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -399,6 +399,31 @@ index_am_handler_out(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();			/* keep compiler quiet */
 }
 
+/*
+ * seq_am_handler_int		- input routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_in(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * seq_am_handler_out		- output routine for pseudo-type SEQ_AM_HANDLER.
+ */
+Datum
+seq_am_handler_out(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot display a value of type seq_am_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
 
 /*
  * tsm_handler_in		- input routine for pseudo-type TSM_HANDLER.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 130c06d..b9686da 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
+#include "access/seqamapi.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -260,6 +261,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -424,6 +426,7 @@ static void
 RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
 	bytea	   *options;
+	amoptions_function	amoptions = NULL;
 
 	relation->rd_options = NULL;
 
@@ -432,10 +435,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	{
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
-		case RELKIND_INDEX:
 		case RELKIND_VIEW:
 		case RELKIND_MATVIEW:
 			break;
+		case RELKIND_INDEX:
+			amoptions = relation->rd_amroutine->amoptions;
+			break;
+		case RELKIND_SEQUENCE:
+			amoptions = relation->rd_seqamroutine->amoptions;
+			break;
 		default:
 			return;
 	}
@@ -447,8 +455,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	 */
 	options = extractRelOptions(tuple,
 								GetPgClassDescriptor(),
-								relation->rd_rel->relkind == RELKIND_INDEX ?
-								relation->rd_amroutine->amoptions : NULL);
+								amoptions);
 
 	/*
 	 * Copy parsed data into CacheMemoryContext.  To guard against the
@@ -1049,11 +1056,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->rd_rsdesc = NULL;
 
-	/*
-	 * if it's an index, initialize index-related information
-	 */
-	if (OidIsValid(relation->rd_rel->relam))
+	/* if it's an index, initialize index-related information */
+	if (relation->rd_rel->relkind == RELKIND_INDEX &&
+		OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
+	/* same for sequences */
+	else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+			 OidIsValid(relation->rd_rel->relam))
+		RelationInitSequenceAccessInfo(relation);
 
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
@@ -1555,6 +1565,34 @@ LookupOpclassInfo(Oid operatorClassOid,
 	return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation relation)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	SeqAmRoutine	   *result, *tmp;
+
+	/*
+	 * Look up the index's access method, save the OID of its handler function
+	 */
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 relation->rd_rel->relam);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amtype == AMTYPE_SEQUENCE);
+	relation->rd_amhandler = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	result = (SeqAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+												 sizeof(SeqAmRoutine));
+	tmp = GetSeqAmRoutine(relation->rd_amhandler);
+	memcpy(result, tmp, sizeof(SeqAmRoutine));
+	relation->rd_seqamroutine = result;
+}
 
 /*
  *		formrdesc
@@ -4885,6 +4923,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_seqamroutine == NULL);
 		}
 
 		/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e48e412..a4e45c4 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqamapi.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 454225d..6e6f5a0 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4747,6 +4747,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relreplident;
 	int			i_owning_tab;
 	int			i_owning_col;
+	int			i_relam;
 	int			i_reltablespace;
 	int			i_reloptions;
 	int			i_checkoption;
@@ -4798,6 +4799,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4840,6 +4842,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4882,6 +4885,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4924,6 +4928,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -4964,6 +4969,7 @@ getTables(Archive *fout, int *numTables)
 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5003,6 +5009,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "tc.reloptions AS toast_reloptions "
@@ -5042,6 +5049,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "c.reloptions AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5081,6 +5089,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5119,6 +5128,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "d.refobjid AS owning_tab, "
 						  "d.refobjsubid AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5153,6 +5163,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5182,6 +5193,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5221,6 +5233,7 @@ getTables(Archive *fout, int *numTables)
 						  "NULL AS reloftype, "
 						  "NULL::oid AS owning_tab, "
 						  "NULL::int4 AS owning_col, "
+						  "c.relam, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
 						  "NULL AS toast_reloptions "
@@ -5274,6 +5287,7 @@ getTables(Archive *fout, int *numTables)
 	i_relpages = PQfnumber(res, "relpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
+	i_relam = PQfnumber(res, "relam");
 	i_reltablespace = PQfnumber(res, "reltablespace");
 	i_reloptions = PQfnumber(res, "reloptions");
 	i_checkoption = PQfnumber(res, "checkoption");
@@ -5339,6 +5353,10 @@ getTables(Archive *fout, int *numTables)
 			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
 			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
 		}
+		if (PQgetisnull(res, i, i_relam))
+			tblinfo[i].relam = InvalidOid;
+		else
+			tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
 		tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
 		tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
 		if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -11577,6 +11595,9 @@ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
 		case AMTYPE_INDEX:
 			appendPQExpBuffer(q, "TYPE INDEX ");
 			break;
+		case AMTYPE_SEQUENCE:
+			appendPQExpBuffer(q, "TYPE SEQUENCE ");
+			break;
 		default:
 			write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
 					  aminfo->amtype, qamname);
@@ -15317,7 +15338,8 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
-			   *cache;
+			   *cache,
+			   *amname = NULL;
 	char		bufm[100],
 				bufx[100];
 	bool		cycled;
@@ -15334,8 +15356,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	if (fout->remoteVersion >= 80400)
 	{
 		appendPQExpBuffer(query,
-						  "SELECT sequence_name, "
-						  "start_value, increment_by, "
+						  "SELECT start_value, increment_by, "
 				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
 				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
 						  "     ELSE max_value "
@@ -15351,8 +15372,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	else
 	{
 		appendPQExpBuffer(query,
-						  "SELECT sequence_name, "
-						  "0 AS start_value, increment_by, "
+						  "SELECT 0 AS start_value, increment_by, "
 				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
 				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
 						  "     ELSE max_value "
@@ -15377,24 +15397,40 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 		exit_nicely(1);
 	}
 
-	/* Disable this check: it fails if sequence has been renamed */
-#ifdef NOT_USED
-	if (strcmp(PQgetvalue(res, 0, 0), tbinfo->dobj.name) != 0)
+	startv = PQgetvalue(res, 0, 0);
+	incby = PQgetvalue(res, 0, 1);
+	if (!PQgetisnull(res, 0, 2))
+		maxv = PQgetvalue(res, 0, 2);
+	if (!PQgetisnull(res, 0, 3))
+		minv = PQgetvalue(res, 0, 3);
+	cache = PQgetvalue(res, 0, 4);
+	cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0);
+
+	/* 9.6 adds sequence access methods */
+	if (fout->remoteVersion >= 90600)
 	{
-		write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
-				  tbinfo->dobj.name, PQgetvalue(res, 0, 0));
-		exit_nicely(1);
-	}
-#endif
+		PGresult   *res2;
 
-	startv = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
-	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
-	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
-	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
+		printfPQExpBuffer(query, "SELECT a.amname\n"
+								 "FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								 "WHERE c.relam = a.oid AND c.oid = %u",
+						  tbinfo->dobj.catId.oid);
+
+		res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+		if (PQntuples(res2) != 1)
+		{
+			write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+									 "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+									 PQntuples(res2)),
+					  tbinfo->dobj.name, PQntuples(res2));
+			exit_nicely(1);
+		}
+
+		amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+		PQclear(res2);
+	}
 
 	/*
 	 * DROP must be fully qualified in case same name appears in pg_catalog
@@ -15437,6 +15473,13 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 					  "    CACHE %s%s",
 					  cache, (cycled ? "\n    CYCLE" : ""));
 
+	/*
+	 * Only produce using when it makes sense,
+	 * this helps with backwards compatibility.
+	 */
+	if (amname)
+		appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
 	appendPQExpBufferStr(query, ";\n");
 
 	appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -15503,6 +15546,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+	if (amname)
+		free(amname);
+
 	PQclear(res);
 
 	destroyPQExpBuffer(query);
@@ -15519,16 +15565,23 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
 	TableInfo  *tbinfo = tdinfo->tdtable;
 	PGresult   *res;
-	char	   *last;
-	bool		called;
 	PQExpBuffer query = createPQExpBuffer();
 
 	/* Make sure we are in proper schema */
 	selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-	appendPQExpBuffer(query,
-					  "SELECT last_value, is_called FROM %s",
-					  fmtId(tbinfo->dobj.name));
+	/* On 9.6 there is special interface for dumping sequences */
+	if (fout->remoteVersion >= 90600)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+		appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+		appendPQExpBuffer(query, "))");
+	}
+	else
+		appendPQExpBuffer(query,
+						  "SELECT last_value, is_called FROM %s",
+						  fmtId(tbinfo->dobj.name));
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -15541,14 +15594,27 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 		exit_nicely(1);
 	}
 
-	last = PQgetvalue(res, 0, 0);
-	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
 	resetPQExpBuffer(query);
-	appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-	appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-	appendPQExpBuffer(query, ", %s, %s);\n",
-					  last, (called ? "true" : "false"));
+
+	if (fout->remoteVersion >= 90600)
+	{
+		char   *state = PQgetvalue(res, 0, 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		/* The state got quote in the SELECT. */
+		appendPQExpBuffer(query, ", %s);\n", state);
+	}
+	else
+	{
+		char   *last = PQgetvalue(res, 0, 0);
+		bool	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+		appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+		appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+		appendPQExpBuffer(query, ", %s, %s);\n",
+						  last, (called ? "true" : "false"));
+	}
 
 	ArchiveEntry(fout, nilCatalogId, createDumpId(),
 				 tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index c02c536..c62abcc 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -231,6 +231,7 @@ typedef struct _tableInfo
 	/* these two are set only if table is a sequence owned by a column: */
 	Oid			owning_tab;		/* OID of table owning sequence */
 	int			owning_col;		/* attr # of column owning sequence */
+	int			relam;			/* access method (from pg_class) */
 	int			relpages;		/* table's size in pages (from pg_class) */
 
 	bool		interesting;	/* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 7b2f4e6..ac85da3 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1403,30 +1403,6 @@ describeOneTableDetails(const char *schemaname,
 	res = NULL;
 
 	/*
-	 * If it's a sequence, fetch its values and store into an array that will
-	 * be used later.
-	 */
-	if (tableinfo.relkind == 'S')
-	{
-		printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-		/* must be separate because fmtId isn't reentrant */
-		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-		res = PSQLexec(buf.data);
-		if (!res)
-			goto error_return;
-
-		seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-		for (i = 0; i < PQnfields(res); i++)
-			seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-		seq_values[i] = NULL;
-
-		PQclear(res);
-		res = NULL;
-	}
-
-	/*
 	 * Get column info
 	 *
 	 * You need to modify value of "firstvcol" which will be defined below if
@@ -1470,13 +1446,55 @@ describeOneTableDetails(const char *schemaname,
 
 	appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
 	appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+	/*
+	 * For sequence, fetch only the common column unless verbose was specified.
+	 * Note that this is change from pre9.5 versions.
+	 */
+	if (tableinfo.relkind == 'S')
+		appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
 	appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
 	numrows = PQntuples(res);
 
+	/*
+	 * If it's a sequence, fetch its values and store into an array that will
+	 * be used later.
+	 */
+	if (tableinfo.relkind == 'S')
+	{
+		PGresult   *result;
+
+		/*
+		 * Use column names from the column info query, to automatically skip
+		 * unwanted columns.
+		 */
+		printfPQExpBuffer(&buf, "SELECT ");
+		for (i = 0; i < numrows; i++)
+			appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+		appendPQExpBuffer(&buf, " FROM %s",
+						  fmtId(schemaname));
+		/* must be separate because fmtId isn't reentrant */
+		appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+		result = PSQLexec(buf.data);
+		if (!result)
+			goto error_return;
+
+		seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+		for (i = 0; i < PQnfields(result); i++)
+			seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+		seq_values[i] = NULL;
+
+		PQclear(result);
+	}
+
 	/* Make title */
 	switch (tableinfo.relkind)
 	{
@@ -1805,6 +1823,8 @@ describeOneTableDetails(const char *schemaname,
 						  oid);
 
 		result = PSQLexec(buf.data);
+
+		/* Same logic as above, only print result when we get one row. */
 		if (!result)
 			goto error_return;
 		else if (PQntuples(result) == 1)
@@ -1814,12 +1834,56 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, buf.data);
 		}
 
+		PQclear(result);
+
+		/* Get the Access Method name for the sequence */
+		printfPQExpBuffer(&buf, "SELECT a.amname\n"
+								"FROM pg_catalog.pg_am a, pg_catalog.pg_class c\n"
+								"WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+		result = PSQLexec(buf.data);
+
 		/*
 		 * If we get no rows back, don't show anything (obviously). We should
 		 * never get more than one row back, but if we do, just ignore it and
 		 * don't print anything.
 		 */
+		if (!result)
+			goto error_return;
+		else if (PQntuples(result) == 1)
+		{
+			printfPQExpBuffer(&buf, _("Access Method: %s"),
+							  PQgetvalue(result, 0, 0));
+			printTableAddFooter(&cont, buf.data);
+		}
+
 		PQclear(result);
+
+		if (verbose)
+		{
+			/* Get the Access Method state */
+			printfPQExpBuffer(&buf,
+							  "SELECT pg_catalog.pg_sequence_get_state('%s');",
+							  oid);
+
+			result = PSQLexec(buf.data);
+
+			/*
+			 * If we get no rows back, don't show anything (obviously). We should
+			 * never get more than one row back, but if we do, just ignore it and
+			 * don't print anything.
+			 */
+			if (!result)
+				goto error_return;
+			else if (PQntuples(result) == 1)
+			{
+				printfPQExpBuffer(&buf, _("Access Method State: %s"),
+								  PQgetvalue(result, 0, 0));
+				printTableAddFooter(&cont, buf.data);
+			}
+
+			PQclear(result);
+		}
 	}
 	else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
 			 tableinfo.relkind == 'f')
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index cb8a06d..a62291c 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1537,7 +1537,7 @@ psql_completion(const char *text, int start, int end)
 	{
 		static const char *const list_ALTERSEQUENCE[] =
 		{"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
-		"SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", NULL};
+		"SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", "USING", NULL};
 
 		COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
 	}
@@ -2116,8 +2116,8 @@ psql_completion(const char *text, int start, int end)
 /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */
 	else if (TailMatches3("CREATE", "SEQUENCE", MatchAny) ||
 			 TailMatches4("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny))
-		COMPLETE_WITH_LIST8("INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", "CACHE",
-							"CYCLE", "OWNED BY", "START WITH");
+		COMPLETE_WITH_LIST9("INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", "CACHE",
+							"CYCLE", "OWNED BY", "START WITH", "USING");
 	else if (TailMatches4("CREATE", "SEQUENCE", MatchAny, "NO") ||
 		TailMatches5("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO"))
 		COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE");
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 469ac67..69135aa 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -273,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
+extern bytea *am_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqamapi.h b/src/include/access/seqamapi.h
new file mode 100644
index 0000000..a4c826a
--- /dev/null
+++ b/src/include/access/seqamapi.h
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqamapi.h
+ *	  Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqamapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAMAPI_H
+#define SEQAMAPI_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+typedef bytea * (*ParseRelOptions_function) (Datum reloptions,
+											 bool validate);
+
+typedef Datum (*SeqAMInit_function) (Relation seqrel,
+									 Form_pg_sequence seq,
+									 int64 restart_value,
+									 bool restart_requested,
+									 bool is_init);
+
+typedef int64 (*SeqAMAlloc_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 nrequested,
+									  int64 *last);
+
+typedef void (*SeqAMSetval_function) (Relation seqrel,
+									  SequenceHandle *seqh,
+									  int64 new_value);
+
+typedef Datum (*SeqAMGetState_function) (Relation seqrel,
+										 SequenceHandle *seqh);
+typedef void (*SeqAMSetState_function) (Relation seqrel, SequenceHandle *seqh,
+										Datum amstate);
+
+typedef struct SeqAmRoutine
+{
+	NodeTag		type;
+
+	/* Custom columns needed by the AM */
+	Oid			StateTypeOid;
+
+	/* Function for parsing reloptions */
+	ParseRelOptions_function	amoptions;
+
+	/* Initialization */
+	SeqAMInit_function			Init;
+
+	/* nextval handler */
+	SeqAMAlloc_function			Alloc;
+
+	/* State manipulation functions */
+	SeqAMSetval_function		Setval;
+	SeqAMGetState_function		GetState;
+	SeqAMSetState_function		SetState;
+} SeqAmRoutine;
+
+extern SeqAmRoutine *GetSeqAmRoutine(Oid seqamhandler);
+extern SeqAmRoutine *GetSeqAmRoutineByAMId(Oid amoid);
+extern SeqAmRoutine *GetSeqAmRoutineForRelation(Relation relation);
+extern void ValidateSeqAmRoutine(SeqAmRoutine *routine);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern Form_pg_sequence sequence_read_options(SequenceHandle *seqh);
+extern Datum sequence_read_state(SequenceHandle *seqh);
+extern void sequence_start_update(SequenceHandle *seqh, bool dowal);
+extern void sequence_save_state(SequenceHandle *seqh, Datum seqstate,
+								bool dowal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+								int64 minv, int64 maxv, int64 incby,
+								bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+								 int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+										bool *found);
+
+/* We need this public for serval3 */
+typedef struct LocalSequenceState
+{
+	int64   last_value;
+	int32	log_cnt;
+	bool	is_called;
+} LocalSequenceState;
+
+extern Datum seqam_local_state_in(PG_FUNCTION_ARGS);
+extern Datum seqam_local_state_out(PG_FUNCTION_ARGS);
+extern Datum seqam_local_handler(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAMAPI_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..d6b3cd7 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods,
 						 bool is_internal,
+						 Oid relam,
 						 ObjectAddress *typaddress);
 
 extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 1116923..9c615f7 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
  * ----------------
  */
 #define AMTYPE_INDEX					'i'		/* index access method */
+#define	AMTYPE_SEQUENCE					'S'		/* sequence access method */
 
 /* ----------------
  *		initial contents of pg_am
@@ -84,4 +85,8 @@ DATA(insert OID = 3580 (  brin		brinhandler i ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
+DATA(insert OID = 6023 (  local		seqam_local_handler	S ));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6023
+
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c86b920..54a712c 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1759,6 +1759,10 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v u 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 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_ _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 (  pg_sequence_get_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 25 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 (  pg_sequence_set_state		PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_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 s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -3690,6 +3694,10 @@ DATA(insert OID = 326  (  index_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f
 DESCR("I/O");
 DATA(insert OID = 327  (  index_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
 DESCR("I/O");
+DATA(insert OID = 6021  (  seq_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 6020 "2275" _null_ _null_ _null_ _null_ _null_ seq_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6022 (  seq_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6020" _null_ _null_ _null_ _null_ _null_ seq_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
 DATA(insert OID = 3311 (  tsm_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 3312 (  tsm_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
@@ -3701,6 +3709,10 @@ DESCR("BERNOULLI tablesample method handler");
 DATA(insert OID = 3314 (  system			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_system_handler _null_ _null_ _null_ ));
 DESCR("SYSTEM tablesample method handler");
 
+/* sequence access method handlers */
+DATA(insert OID = 6024 (  seqam_local_handler	   PGNSP PGUID 12 1 0 0 0 f f f f f f v s 1 0 6020 "2281" _null_ _null_ _null_ _null_ _null_ seqam_local_handler _null_ _null_ _null_ ));
+DESCR("Local SequenceAM handler");
+
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
 DESCR("MD5 hash");
@@ -5270,6 +5282,13 @@ DESCR("pg_controldata recovery state information as a function");
 DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
 DESCR("pg_controldata init state information as a function");
 
+/* Sequence AM */
+DATA(insert OID = 6027 (  seqam_local_state_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 6025 "2275" _null_ _null_ _null_ _null_ _null_ seqam_local_state_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 6028 (  seqam_local_state_out		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "6025" _null_ _null_ _null_ _null_ _null_ seqam_local_state_out _null_ _null_ _null_ ));
+DESCR("I/O");
+
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2c90b76..7f020fe 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -696,11 +696,18 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 #define FDW_HANDLEROID	3115
 DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define INDEX_AM_HANDLEROID	325
+DATA(insert OID = 6020 ( seq_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 seq_am_handler_in seq_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQ_AM_HANDLEROID	6020
 DATA(insert OID = 3310 ( tsm_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
 #define TSM_HANDLEROID	3310
 DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 #define ANYRANGEOID		3831
 
+/* Sequence AM composite types */
+DATA(insert OID = 6025 ( seqam_local_state	PGNSP PGUID 16 f b U f t \054 0 0 6026 seqam_local_state_in seqam_local_state_out - - - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define SEQAMLOCALSTATEOID 6025
+DATA(insert OID = 6026 ( _seqam_local_state	PGNSP PGUID -1 f b A f t \054 0 6025 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 
 /*
  * macros
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index b064eb4..c9bf431 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -139,6 +139,7 @@ extern Datum transformGenericOptions(Oid catalogId,
 extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
 extern void RemoveAccessMethodById(Oid amOid);
 extern Oid	get_index_am_oid(const char *amname, bool missing_ok);
+extern Oid	get_seq_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);
 
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..2ff89b4 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -19,20 +19,17 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "storage/relfilenode.h"
-
+#include "access/htup_details.h"
 
 typedef struct FormData_pg_sequence
 {
-	NameData	sequence_name;
-	int64		last_value;
 	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
 	int64		cache_value;
-	int64		log_cnt;
 	bool		is_cycled;
-	bool		is_called;
+	/* amstate follows */
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -41,19 +38,16 @@ typedef FormData_pg_sequence *Form_pg_sequence;
  * Columns of a sequence relation
  */
 
-#define SEQ_COL_NAME			1
-#define SEQ_COL_LASTVAL			2
-#define SEQ_COL_STARTVAL		3
-#define SEQ_COL_INCBY			4
-#define SEQ_COL_MAXVALUE		5
-#define SEQ_COL_MINVALUE		6
-#define SEQ_COL_CACHE			7
-#define SEQ_COL_LOG				8
-#define SEQ_COL_CYCLE			9
-#define SEQ_COL_CALLED			10
+#define SEQ_COL_STARTVAL		1
+#define SEQ_COL_INCBY			2
+#define SEQ_COL_MAXVALUE		3
+#define SEQ_COL_MINVALUE		4
+#define SEQ_COL_CACHE			5
+#define SEQ_COL_CYCLE			6
+#define SEQ_COL_AMSTATE			7
 
-#define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
+#define SEQ_COL_FIRSTCOL		SEQ_COL_STARTVAL
+#define SEQ_COL_LASTCOL			SEQ_COL_AMSTATE
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG			0x00
@@ -72,6 +66,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 7a770f4..1bceec8 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
 
 
 extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
-			   ObjectAddress *typaddress);
+			   Oid relamid, ObjectAddress *typaddress);
 
 extern void RemoveRelations(DropStmt *drop);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 734df77..752d3a1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -477,6 +477,7 @@ typedef enum NodeTag
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
+	T_SeqAmRoutine,				/* in access/seqamapi.h */
 	T_TsmRoutine				/* in access/tsmapi.h */
 } NodeTag;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8b958b4..859fbad 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2205,8 +2205,10 @@ typedef struct CreateSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to create */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2214,8 +2216,10 @@ typedef struct AlterSeqStmt
 {
 	NodeTag		type;
 	RangeVar   *sequence;		/* the sequence to alter */
-	List	   *options;
+	List       *options;        /* standard sequence options */
+	List	   *amoptions;		/* am specific options */
 	bool		missing_ok;		/* skip error if a role is missing? */
+	char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index d5c4b01..cf7e01b 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -587,6 +587,8 @@ extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
 extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
 extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_in(PG_FUNCTION_ARGS);
+extern Datum seq_am_handler_out(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
 extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
 extern Datum internal_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f2bebf2..4fe7e52 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -106,13 +106,18 @@ typedef struct RelationData
 	 */
 	bytea	   *rd_options;		/* parsed pg_class.reloptions */
 
+	Oid			rd_amhandler;	/* OID of access method's handler function */
+
+	/* These are non-NULL only for a sequence relation */
+	struct SeqAmRoutine *rd_seqamroutine;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
 	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
 
 	/*
-	 * index access support info (used only for an index relation)
+	 * index access support info (used only for index relations)
 	 *
 	 * Note: only default support procs for each opclass are cached, namely
 	 * those with lefttype and righttype equal to the opclass's opcintype. The
@@ -126,7 +131,6 @@ typedef struct RelationData
 	 * rd_indexcxt.  A relcache reset will include freeing that chunk and
 	 * setting rd_amcache = NULL.
 	 */
-	Oid			rd_amhandler;	/* OID of index AM's handler function */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
 	/* use "struct" here to avoid needing to include amapi.h: */
 	struct IndexAmRoutine *rd_amroutine;		/* index AM's API struct */
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index 47d6024..9242982 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -106,3 +106,22 @@ drop cascades to index grect2ind
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+-- Checks for sequence access methods
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+ERROR:  cannot drop access method local2 because other objects depend on it
+DETAIL:  sequence test_seqam depends on access method local2
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index ae50c94..f1b1fc2 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -129,8 +129,9 @@ NOTICE:  view "v12_temp" will be a temporary view
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 NOTICE:  view "v13_temp" will be a temporary view
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index a8dc0c1..1c51a5e 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1586,7 +1586,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
  oid | amname | oid | proname 
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3a19cde 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
+ pg_sequence_set_state 
+-----------------------
+ 
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate 
+-------------+--------------+---------------------+-----------+-------------+-----------+---------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (1,f,0)
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | amstate  
+-------------+--------------+---------------------+-----------+-------------+-----------+----------
+           1 |            1 | 9223372036854775807 |         1 |           1 | f         | (2,t,31)
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
        5
 (1 row)
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR:  local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR:  local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c5dfbb5..a30582e 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,52 @@ SELECT table_name, column_name, is_updatable
   FROM information_schema.columns
  WHERE table_name LIKE E'r_\\_view%'
  ORDER BY table_name, ordinal_position;
- table_name |  column_name  | is_updatable 
-------------+---------------+--------------
- ro_view1   | a             | NO
- ro_view1   | b             | NO
- ro_view10  | a             | NO
- ro_view11  | a             | NO
- ro_view11  | b             | NO
- ro_view12  | a             | NO
- ro_view13  | a             | NO
- ro_view13  | b             | NO
- ro_view17  | a             | NO
- ro_view17  | b             | NO
- ro_view18  | a             | NO
- ro_view19  | sequence_name | NO
- ro_view19  | last_value    | NO
- ro_view19  | start_value   | NO
- ro_view19  | increment_by  | NO
- ro_view19  | max_value     | NO
- ro_view19  | min_value     | NO
- ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
- ro_view19  | is_cycled     | NO
- ro_view19  | is_called     | NO
- ro_view2   | a             | NO
- ro_view2   | b             | NO
- ro_view20  | a             | NO
- ro_view20  | b             | NO
- ro_view20  | g             | NO
- ro_view3   | ?column?      | NO
- ro_view4   | count         | NO
- ro_view5   | a             | NO
- ro_view5   | rank          | NO
- ro_view6   | a             | NO
- ro_view6   | b             | NO
- ro_view7   | a             | NO
- ro_view7   | b             | NO
- ro_view8   | a             | NO
- ro_view8   | b             | NO
- ro_view9   | a             | NO
- ro_view9   | b             | NO
- rw_view14  | ctid          | NO
- rw_view14  | a             | YES
- rw_view14  | b             | YES
- rw_view15  | a             | YES
- rw_view15  | upper         | NO
- rw_view16  | a             | YES
- rw_view16  | b             | YES
- rw_view16  | aa            | YES
-(46 rows)
+ table_name | column_name  | is_updatable 
+------------+--------------+--------------
+ ro_view1   | a            | NO
+ ro_view1   | b            | NO
+ ro_view10  | a            | NO
+ ro_view11  | a            | NO
+ ro_view11  | b            | NO
+ ro_view12  | a            | NO
+ ro_view13  | a            | NO
+ ro_view13  | b            | NO
+ ro_view17  | a            | NO
+ ro_view17  | b            | NO
+ ro_view18  | a            | NO
+ ro_view19  | start_value  | NO
+ ro_view19  | increment_by | NO
+ ro_view19  | max_value    | NO
+ ro_view19  | min_value    | NO
+ ro_view19  | cache_value  | NO
+ ro_view19  | is_cycled    | NO
+ ro_view19  | amstate      | NO
+ ro_view2   | a            | NO
+ ro_view2   | b            | NO
+ ro_view20  | a            | NO
+ ro_view20  | b            | NO
+ ro_view20  | g            | NO
+ ro_view3   | ?column?     | NO
+ ro_view4   | count        | NO
+ ro_view5   | a            | NO
+ ro_view5   | rank         | NO
+ ro_view6   | a            | NO
+ ro_view6   | b            | NO
+ ro_view7   | a            | NO
+ ro_view7   | b            | NO
+ ro_view8   | a            | NO
+ ro_view8   | b            | NO
+ ro_view9   | a            | NO
+ ro_view9   | b            | NO
+ rw_view14  | ctid         | NO
+ rw_view14  | a            | YES
+ rw_view14  | b            | YES
+ rw_view15  | a            | YES
+ rw_view15  | upper        | NO
+ rw_view16  | a            | YES
+ rw_view16  | b            | YES
+ rw_view16  | aa           | YES
+(43 rows)
 
 -- Read-only views
 DELETE FROM ro_view1;
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index e2051c5..e811ea3 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -71,3 +71,19 @@ DROP ACCESS METHOD gist2 CASCADE;
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+
+-- Checks for sequence access methods
+
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seqam_local_handler;
+
+-- Create and use sequence
+CREATE SEQUENCE test_seqam USING local2;
+SELECT nextval('test_seqam'::regclass);
+
+-- Try to drop and fail on dependency
+DROP ACCESS METHOD local2;
+
+-- And cleanup
+DROP SEQUENCE test_seqam;
+DROP ACCESS METHOD local2;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 58d361d..e967fff 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -131,8 +131,9 @@ CREATE VIEW v12_temp AS SELECT true FROM v11_temp;
 -- a view should also be temporary if it references a temporary sequence
 CREATE SEQUENCE seq1;
 CREATE TEMPORARY SEQUENCE seq1_temp;
-CREATE VIEW v9 AS SELECT seq1.is_called FROM seq1;
-CREATE VIEW v13_temp AS SELECT seq1_temp.is_called FROM seq1_temp;
+CREATE TYPE seqam_local_state_rec AS (last_value int8, is_called bool, log_cnt int4);
+CREATE VIEW v9 AS SELECT (seq1::text::seqam_local_state_rec).is_called FROM seq1;
+CREATE VIEW v13_temp AS SELECT (seq1_temp::text::seqam_local_state_rec).is_called FROM seq1_temp;
 
 SELECT relname FROM pg_class
     WHERE relname LIKE 'v_'
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 6c9784a..6c44d70 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1055,7 +1055,9 @@ WHERE p1.amhandler = 0;
 SELECT p1.oid, p1.amname, p2.oid, p2.proname
 FROM pg_am AS p1, pg_proc AS p2
 WHERE p2.oid = p1.amhandler AND
-    (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+    (p2.prorettype NOT IN ('index_am_handler'::regtype,
+			               'seq_am_handler'::regtype)
+     OR p2.proretset
      OR p2.pronargs != 1
      OR p2.proargtypes[0] != 'internal'::regtype);
 
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..c2b1164 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '(99,false)');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '(99,false)');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
 -- Information schema
 SELECT * FROM information_schema.sequences WHERE sequence_name IN
   ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
0002-gapless-seq-2016-04-05.patchtext/x-diff; charset=US-ASCII; name=0002-gapless-seq-2016-04-05.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
index 25263c0..991ee7b 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -20,6 +20,7 @@ SUBDIRS = \
 		earthdistance	\
 		file_fdw	\
 		fuzzystrmatch	\
+		gapless_seq	\
 		hstore		\
 		intagg		\
 		intarray	\
diff --git a/contrib/gapless_seq/Makefile b/contrib/gapless_seq/Makefile
new file mode 100644
index 0000000..9378b93
--- /dev/null
+++ b/contrib/gapless_seq/Makefile
@@ -0,0 +1,63 @@
+# contrib/gapless_seq/Makefile
+
+MODULE_big = gapless_seq
+OBJS = gapless_seq.o
+PG_CPPFLAGS = -I$(libpq_srcdir)
+
+EXTENSION = gapless_seq
+DATA = gapless_seq--1.0.sql
+
+EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gapless_seq
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+check: regresscheck isolationcheck
+
+submake-regress:
+	$(MAKE) -C $(top_builddir)/src/test/regress all
+
+submake-isolation:
+	$(MAKE) -C $(top_builddir)/src/test/isolation all
+
+submake-gapless_seq:
+	$(MAKE) -C $(top_builddir)/contrib/gapless_seq
+
+REGRESSCHECKS=gapless_seq
+
+regresscheck: all | submake-regress submake-gapless_seq temp-install
+	$(MKDIR_P) regression_output
+	$(pg_regress_check) \
+	    --temp-instance=./tmp_check \
+	    --outputdir=./regression_output \
+	    $(REGRESSCHECKS)
+
+regresscheck-install-force: | submake-regress submake-gapless_seq temp-install
+	$(pg_regress_installcheck) \
+	    $(REGRESSCHECKS)
+
+ISOLATIONCHECKS=concurrency
+
+isolationcheck: all | submake-isolation submake-gapless_seq temp-install
+	$(MKDIR_P) isolation_output
+	$(pg_isolation_regress_check) \
+	    --outputdir=./isolation_output \
+	    $(ISOLATIONCHECKS)
+
+isolationcheck-install-force: all | submake-isolation submake-gapless_seq temp-install
+	$(pg_isolation_regress_installcheck) \
+	    $(ISOLATIONCHECKS)
+
+PHONY: submake-gapless_seq submake-regress check \
+	regresscheck regresscheck-install-force \
+	isolationcheck isolationcheck-install-force
+
+temp-install: EXTRA_INSTALL=contrib/gapless_seq
diff --git a/contrib/gapless_seq/expected/concurrency.out b/contrib/gapless_seq/expected/concurrency.out
new file mode 100644
index 0000000..ec6a098
--- /dev/null
+++ b/contrib/gapless_seq/expected/concurrency.out
@@ -0,0 +1,31 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_begin s1_nextval s2_begin s2_nextval s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s1_commit: COMMIT;
+step s2_nextval: <... completed>
+nextval        
+
+2              
+step s2_commit: COMMIT;
+
+starting permutation: s3_begin s3_nextval s2_begin s2_nextval s3_rollback s2_commit
+step s3_begin: BEGIN;
+step s3_nextval: SELECT nextval('test_gapless'::regclass);
+nextval        
+
+1              
+step s2_begin: BEGIN;
+step s2_nextval: SELECT nextval('test_gapless'::regclass); <waiting ...>
+step s3_rollback: ROLLBACK;
+step s2_nextval: <... completed>
+nextval        
+
+1              
+step s2_commit: COMMIT;
diff --git a/contrib/gapless_seq/expected/gapless_seq.out b/contrib/gapless_seq/expected/gapless_seq.out
new file mode 100644
index 0000000..130104a
--- /dev/null
+++ b/contrib/gapless_seq/expected/gapless_seq.out
@@ -0,0 +1,145 @@
+CREATE EXTENSION gapless_seq;
+CREATE SEQUENCE test_gapless USING gapless;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+	SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_gapless'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+CREATE SEQUENCE test_alter_seq USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       1
+(1 row)
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       2
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+ROLLBACK;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       3
+(1 row)
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       4
+(1 row)
+
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       5
+(1 row)
+
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+       6
+(1 row)
+
+COMMIT;
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+SELECT nextval('test_alter_seq'::regclass);
+ nextval 
+---------
+     100
+(1 row)
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+ pg_sequence_set_state 
+-----------------------
+ 
+(1 row)
+
+SELECT pg_sequence_get_state('test_gapless');
+ pg_sequence_get_state 
+-----------------------
+ (2,t)
+(1 row)
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+          2
+          6
+(2 rows)
+
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+ last_value 
+------------
+(0 rows)
+
+CREATE SEQUENCE test_gapless USING gapless;
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+ERROR:  cannot drop access method gapless because extension gapless_seq requires it
+HINT:  You can drop extension gapless_seq instead.
+-- likewise
+DROP EXTENSION gapless_seq;
+ERROR:  cannot drop extension gapless_seq because other objects depend on it
+DETAIL:  sequence test_gapless depends on access method gapless
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- success
+DROP EXTENSION gapless_seq CASCADE;
+NOTICE:  drop cascades to sequence test_gapless
diff --git a/contrib/gapless_seq/gapless_seq--1.0.sql b/contrib/gapless_seq/gapless_seq--1.0.sql
new file mode 100644
index 0000000..99d6021
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq--1.0.sql
@@ -0,0 +1,58 @@
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gapless_seq" to load this file. \quit
+
+CREATE OR REPLACE FUNCTION seqam_gapless_handler(internal)
+RETURNS SEQ_AM_HANDLER
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_in(cstring)
+RETURNS seqam_gapless_state
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE OR REPLACE FUNCTION seqam_gapless_state_out(seqam_gapless_state)
+RETURNS cstring
+LANGUAGE C
+STABLE STRICT
+AS 'MODULE_PATHNAME'
+;
+
+CREATE TYPE seqam_gapless_state (
+	INPUT   = seqam_gapless_state_in,
+	OUTPUT  = seqam_gapless_state_out,
+	INTERNALLENGTH = 16,
+	ALIGNMENT = double
+);
+COMMENT ON TYPE seqam_gapless_state IS 'state for gapless sequence am';
+
+CREATE TABLE seqam_gapless_values (
+	seqid oid PRIMARY KEY,
+	last_value bigint NOT NULL,
+	is_called bool NOT NULL
+);
+
+CREATE OR REPLACE FUNCTION gapless_seq_clean_sequence_value()
+RETURNS event_trigger
+LANGUAGE plpgsql
+AS $$
+BEGIN
+	-- just delete all the value data that don't have corresponding
+	-- gapless sequence (either DELETEd or ALTERed to different AM)
+	DELETE FROM gapless_seq.seqam_gapless_values WHERE seqid NOT IN (
+		SELECT oid FROM pg_class WHERE relkind = 'S' AND relam = (
+			SELECT oid FROM pg_am WHERE amname = 'gapless' AND amtype = 'S'
+		)
+	);
+END;
+$$;
+
+CREATE EVENT TRIGGER gapless_seq_clean_sequence_value ON sql_drop
+	WHEN TAG IN ('DROP SEQUENCE')
+	EXECUTE PROCEDURE gapless_seq_clean_sequence_value();
+
+CREATE ACCESS METHOD gapless TYPE SEQUENCE HANDLER seqam_gapless_handler;
diff --git a/contrib/gapless_seq/gapless_seq.c b/contrib/gapless_seq/gapless_seq.c
new file mode 100644
index 0000000..bbc3a91
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.c
@@ -0,0 +1,538 @@
+/*-------------------------------------------------------------------------
+ *
+ * gapless_seq.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  contrib/gapless_seq/gapless_seq.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/seqamapi.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "storage/procarray.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+PG_MODULE_MAGIC;
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager / Gapless sequence implementation
+ *
+ *------------------------------------------------------------
+ */
+
+typedef struct GaplessSequenceState
+{
+	int64   		last_value;
+	TransactionId	xid;
+	bool			is_called;
+} GaplessSequenceState;
+
+typedef struct GaplessSequence
+{
+	FormData_pg_sequence	seq;
+	GaplessSequenceState	state;
+} GaplessSequence;
+
+typedef struct GaplessValue
+{
+	Oid		seqid;
+	int64	last_value;
+	bool	is_called;
+} GaplessValue;
+
+#define GAPLESS_SEQ_NAMESPACE "gapless_seq"
+#define VALUES_TABLE_NAME "seqam_gapless_values"
+#define VALUES_TABLE_COLUMNS 3
+
+static bytea *seqam_gapless_reloptions(Datum reloptions, bool validate);
+static Datum seqam_gapless_init(Relation seqrel, Form_pg_sequence seq,
+								int64 restart_value, bool restart_requested,
+								bool is_init);
+static int64 seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh,
+								 int64 nrequested, int64 *last);
+static void seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh,
+								 int64 new_value);
+static Datum seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh);
+static void seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+									Datum amstate);
+
+PG_FUNCTION_INFO_V1(seqam_gapless_handler);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_in);
+PG_FUNCTION_INFO_V1(seqam_gapless_state_out);
+
+static FormData_pg_sequence *wait_for_sequence(SequenceHandle *seqh,
+											   TransactionId local_xid);
+static Relation open_values_rel(void);
+static HeapTuple get_last_value_tup(Relation rel, Oid seqid);
+static void set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+							   int64 last_value, bool is_called);
+
+Datum
+seqam_gapless_state_in(PG_FUNCTION_ARGS)
+{
+	char	   *state_str = PG_GETARG_CSTRING(0);
+	char	   *ptr, *end;
+	Datum		d;
+	GaplessSequenceState *state = palloc(sizeof(LocalSequenceState));
+
+	ptr = state_str;
+	while (*ptr && isspace((unsigned char) *ptr))
+		ptr++;
+
+	if (*ptr != '(')
+		goto malformed;
+	ptr++;
+
+	end = ptr;
+	while (*end != ',' && *end != ')')
+	{
+		char		ch = *end++;
+
+		if (ch == '\0')
+			goto malformed;
+	}
+
+	d = DirectFunctionCall1(int8in,
+							CStringGetDatum(pnstrdup(ptr, end-ptr)));
+	state->last_value = DatumGetInt64(d);
+
+	/* is_called is optional */
+	if (*end == ',')
+	{
+		ptr = ++end;
+
+		end = state_str+strlen(state_str)-1;
+
+		if (*end != ')')
+			goto malformed;
+
+		d = DirectFunctionCall1(boolin,
+								CStringGetDatum(pnstrdup(ptr, end-ptr)));
+		state->is_called = DatumGetBool(d);
+	}
+	else
+		state->is_called = true;
+
+	state->xid = InvalidTransactionId;
+
+	PG_RETURN_POINTER(state);
+
+malformed:
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("malformed seqam_gapless_state literal: \"%s\"",
+					state_str)));
+}
+
+Datum
+seqam_gapless_state_out(PG_FUNCTION_ARGS)
+{
+	GaplessSequenceState   *state;
+	StringInfoData			buf;
+
+	state = (GaplessSequenceState *) PG_GETARG_POINTER(0);
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "("INT64_FORMAT",%s)",
+					 state->last_value, state->is_called ? "t" : "f");
+
+	PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Handler function for the gapless sequence access method.
+ */
+Datum
+seqam_gapless_handler(PG_FUNCTION_ARGS)
+{
+	SeqAmRoutine   *seqam = makeNode(SeqAmRoutine);
+	Oid				nspid,
+					typid;
+
+	nspid = get_namespace_oid("gapless_seq", false);
+	typid = GetSysCacheOid2(TYPENAMENSP,
+							PointerGetDatum("seqam_gapless_state"),
+							ObjectIdGetDatum(nspid));
+
+	if (!OidIsValid(typid))
+		elog(ERROR, "cache lookup failed for type \"seqam_gapless_state\"");
+
+	seqam->StateTypeOid = typid;
+	seqam->amoptions = seqam_gapless_reloptions;
+	seqam->Init = seqam_gapless_init;
+	seqam->Alloc = seqam_gapless_alloc;
+	seqam->Setval = seqam_gapless_setval;
+	seqam->GetState = seqam_gapless_get_state;
+	seqam->SetState = seqam_gapless_set_state;
+
+	PG_RETURN_POINTER(seqam);
+}
+
+/*
+ * seqam_gapless_reloptions()
+ *
+ * Parse and verify the reloptions of a gapless sequence.
+ */
+static bytea *
+seqam_gapless_reloptions(Datum reloptions, bool validate)
+{
+	if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("gapless sequence does not accept any storage parameters")));
+
+	return NULL;
+}
+
+/*
+ * seqam_gapless_init()
+ *
+ * Initialize gapless sequence
+ *
+ */
+static Datum
+seqam_gapless_init(Relation seqrel, Form_pg_sequence seq, int64 restart_value,
+				 bool restart_requested, bool is_init)
+{
+	Oid			seqrelid = seqrel->rd_id;
+	Relation	valrel;
+	HeapTuple	valtuple;
+	GaplessSequenceState   *seqstate;
+
+	if (is_init)
+		seqstate = palloc0(sizeof(GaplessSequenceState));
+	else
+		seqstate = &((GaplessSequence *) seq)->state;
+
+	/*
+	 * If this is a new sequence or RESTART was provided in ALTER we should
+	 * reset our state to that new starting point.
+	 */
+	if (is_init || restart_requested)
+	{
+		seqstate->last_value = restart_value;
+		seqstate->is_called = false;
+	}
+
+	seqstate->xid = GetTopTransactionId();
+
+	/* Load current value if this is existing sequence. */
+	valrel = open_values_rel();
+	valtuple = get_last_value_tup(valrel, seqrelid);
+
+	/*
+	 * If this is new sequence or restart was provided or if there is
+	 * no previous stored value for the sequence we should store it in
+	 * the values table.
+	 */
+	if (is_init || restart_requested || !HeapTupleIsValid(valtuple))
+		set_last_value_tup(valrel, seqrelid, valtuple, seqstate->last_value,
+						   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_alloc()
+ *
+ * Allocate new value for gapless sequence.
+ */
+static int64
+seqam_gapless_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+					int64 *last)
+{
+	int64		result;
+	Oid			seqrelid = RelationGetRelid(seqrel);
+	Relation	valrel;
+	HeapTuple	tuple;
+	Form_pg_sequence		seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = &((GaplessSequence *) seq)->state;
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Last value found get next value. */
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		result = v->last_value;
+
+		if (v->is_called)
+			(void) sequence_increment(seqrel, &result, 1, seq->min_value,
+									  seq->max_value,
+									  seq->increment_by,
+									  seq->is_cycled, true);
+	}
+	else /* No last value, start from beginning. */
+		result = seq->start_value;
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, result, true);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* We always WAL log for gapless sequence. */
+	sequence_start_update(seqh, true);
+	seqstate->last_value = result;
+	seqstate->is_called = true;
+	if (!TransactionIdEquals(seqstate->xid, local_xid))
+		seqstate->xid = local_xid;
+	sequence_save_state(seqh, PointerGetDatum(seqstate), true);
+	sequence_finish_update(seqh);
+
+	*last = result;
+
+	return result;
+}
+
+/*
+ * seqam_gapless_setval()
+ *
+ * Setval support (we don't allow setval on gapless)
+ */
+static void
+seqam_gapless_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("setval() is not supported for gapless sequences")));
+}
+
+/*
+ * seqam_gapless_get_state()
+ *
+ * Dump state of a gapless sequence (for pg_dump)
+ */
+static Datum
+seqam_gapless_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	GaplessSequenceState   *seqstate = palloc0(sizeof(GaplessSequenceState));
+
+	/*
+	 * Get the last value from the values table, if not found use the values
+	 * from sequence typle.
+	 */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+	heap_close(valrel, RowExclusiveLock);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		GaplessValue *v = (GaplessValue *) GETSTRUCT(tuple);
+		seqstate->last_value = v->last_value;
+		seqstate->is_called = v->is_called;
+	}
+	else
+	{
+		Datum		amstate = sequence_read_state(seqh);
+
+		memcpy(seqstate, DatumGetPointer(amstate),
+			   sizeof(GaplessSequenceState));
+		sequence_release_tuple(seqh);
+	}
+
+	return PointerGetDatum(seqstate);
+}
+
+/*
+ * seqam_gapless_set_state()
+ *
+ * Restore previously dumpred state of gapless sequence
+ */
+static void
+seqam_gapless_set_state(Relation seqrel, SequenceHandle *seqh,
+						Datum amstate)
+{
+	Oid				seqrelid = RelationGetRelid(seqrel);
+	Relation		valrel;
+	HeapTuple		tuple;
+	FormData_pg_sequence   *seq;
+	GaplessSequenceState   *seqstate;
+	TransactionId local_xid = GetTopTransactionId();
+
+	/* Wait until the sequence is locked by us. */
+	seq = wait_for_sequence(seqh, local_xid);
+	seqstate = (GaplessSequenceState *) DatumGetPointer(amstate);
+
+	sequence_check_range(seqstate->last_value, seq->min_value, seq->max_value,
+						 "last_value");
+
+	/* Read the last value from our transactional table (if any). */
+	valrel = open_values_rel();
+	tuple = get_last_value_tup(valrel, seqrelid);
+
+	/* Insert or update the last value tuple. */
+	set_last_value_tup(valrel, seqrelid, tuple, seqstate->last_value,
+					   seqstate->is_called);
+
+	/* Now we are done with values relation, but keep the lock. */
+	heap_close(valrel, NoLock);
+
+	/* Save to updated sequence. */
+	sequence_start_update(seqh, true);
+	sequence_save_state(seqh, amstate, true);
+	sequence_finish_update(seqh);
+
+	sequence_release_tuple(seqh);
+}
+
+/*
+ * Lock the sequence for current transaction.
+ */
+static FormData_pg_sequence *
+wait_for_sequence(SequenceHandle *seqh, TransactionId local_xid)
+{
+	GaplessSequenceState   *seqstate;
+	Datum					seqstated = sequence_read_state(seqh);
+
+	seqstate = (GaplessSequenceState *) DatumGetPointer(seqstated);
+
+	/*
+	 * Read and lock the sequence for our transaction, there can't be any
+	 * concurrent transactions accessing the sequence at the same time.
+	 */
+	while (seqstate->xid != local_xid &&
+		   TransactionIdIsInProgress(seqstate->xid))
+	{
+		/*
+		 * Release tuple to avoid dead locks and wait for the concurrent tx
+		 * to finish.
+		 */
+		sequence_release_tuple(seqh);
+		XactLockTableWait(seqstate->xid, NULL, NULL, XLTW_None);
+		/* Reread the sequence. */
+		seqstated = sequence_read_state(seqh);
+		seqstate = (GaplessSequenceState *) DatumGetPointer(seqstated);
+	}
+
+	return sequence_read_options(seqh);
+}
+
+/*
+ * Open the relation used for storing last value in RowExclusive lock mode.
+ */
+static Relation
+open_values_rel(void)
+{
+	RangeVar   *rv;
+	Oid			valrelid;
+	Relation	valrel;
+
+	rv = makeRangeVar(GAPLESS_SEQ_NAMESPACE, VALUES_TABLE_NAME, -1);
+	valrelid = RangeVarGetRelid(rv, RowExclusiveLock, false);
+	valrel = heap_open(valrelid, RowExclusiveLock);
+
+	return valrel;
+}
+
+/*
+ * Read the last value tuple from the values table.
+ *
+ * Can return NULL if tuple is not found.
+ */
+static HeapTuple
+get_last_value_tup(Relation rel, Oid seqid)
+{
+	ScanKey		key;
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+
+	key = (ScanKey) palloc(sizeof(ScanKeyData) * 1);
+
+	ScanKeyInit(&key[0],
+				1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(seqid)
+		);
+
+	/* FIXME: should use index */
+	scan = systable_beginscan(rel, 0, true, NULL, 1, key);
+	tuple = systable_getnext(scan);
+	if (HeapTupleIsValid(tuple))
+		tuple = heap_copytuple(tuple);
+	systable_endscan(scan);
+
+	return tuple;
+}
+
+/*
+ * Insert or update the last value tuple.
+ *
+ * The write access to the table must be serialized by the wait_for_sequence
+ * so that we don't have to have any retry scheme here.
+*/
+static void
+set_last_value_tup(Relation rel, Oid seqid, HeapTuple oldtuple,
+				   int64 last_value, bool is_called)
+{
+	bool		nulls[VALUES_TABLE_COLUMNS];
+	Datum		values[VALUES_TABLE_COLUMNS];
+	TupleDesc	tupDesc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+
+	if (!HeapTupleIsValid(oldtuple))
+	{
+		memset(nulls, false, VALUES_TABLE_COLUMNS * sizeof(bool));
+		values[0] = ObjectIdGetDatum(seqid);
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_form_tuple(tupDesc, values, nulls);
+		simple_heap_insert(rel, tuple);
+	}
+	else
+	{
+		bool replaces[VALUES_TABLE_COLUMNS];
+
+		replaces[0] = false;
+		replaces[1] = true;
+		replaces[2] = true;
+
+		nulls[1] = false;
+		nulls[2] = false;
+		values[1] = Int64GetDatum(last_value);
+		values[2] = BoolGetDatum(is_called);
+
+		tuple = heap_modify_tuple(oldtuple, tupDesc, values, nulls, replaces);
+		simple_heap_update(rel, &tuple->t_self, tuple);
+	}
+
+	CatalogUpdateIndexes(rel, tuple);
+}
diff --git a/contrib/gapless_seq/gapless_seq.control b/contrib/gapless_seq/gapless_seq.control
new file mode 100644
index 0000000..85da739
--- /dev/null
+++ b/contrib/gapless_seq/gapless_seq.control
@@ -0,0 +1,6 @@
+# Gapless sequence extension
+comment = 'Gapless Sequence AM'
+default_version = '1.0'
+module_pathname = '$libdir/gapless_seq'
+relocatable = false
+schema = gapless_seq
diff --git a/contrib/gapless_seq/specs/concurrency.spec b/contrib/gapless_seq/specs/concurrency.spec
new file mode 100644
index 0000000..3a0cc57
--- /dev/null
+++ b/contrib/gapless_seq/specs/concurrency.spec
@@ -0,0 +1,29 @@
+setup
+{
+	CREATE EXTENSION IF NOT EXISTS gapless_seq;
+    DROP SEQUENCE IF EXISTS test_gapless;
+	CREATE SEQUENCE test_gapless USING gapless;
+}
+
+teardown
+{
+    DROP SEQUENCE test_gapless;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s2_commit" { COMMIT; }
+
+session "s3"
+step "s3_begin" { BEGIN; }
+step "s3_nextval" { SELECT nextval('test_gapless'::regclass); }
+step "s3_rollback" { ROLLBACK; }
+
+permutation "s1_begin" "s1_nextval" "s2_begin" "s2_nextval" "s1_commit" "s2_commit"
+permutation "s3_begin" "s3_nextval" "s2_begin" "s2_nextval" "s3_rollback" "s2_commit"
diff --git a/contrib/gapless_seq/sql/gapless_seq.sql b/contrib/gapless_seq/sql/gapless_seq.sql
new file mode 100644
index 0000000..b49cec6
--- /dev/null
+++ b/contrib/gapless_seq/sql/gapless_seq.sql
@@ -0,0 +1,61 @@
+CREATE EXTENSION gapless_seq;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+SELECT nextval('test_gapless'::regclass);
+
+BEGIN;
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+	SELECT nextval('test_gapless'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_gapless'::regclass);
+
+CREATE SEQUENCE test_alter_seq USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+ALTER SEQUENCE test_alter_seq USING gapless;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+ROLLBACK;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+BEGIN;
+	SELECT nextval('test_alter_seq'::regclass);
+	SAVEPOINT mysp;
+	SELECT nextval('test_alter_seq'::regclass);
+	ROLLBACK TO SAVEPOINT mysp;
+	SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+	RELEASE SAVEPOINT mysp2;
+	SELECT nextval('test_alter_seq'::regclass);
+COMMIT;
+
+ALTER SEQUENCE test_alter_seq RESTART 100 USING local;
+
+SELECT nextval('test_alter_seq'::regclass);
+
+-- check dump/restore
+SELECT pg_sequence_get_state('test_gapless');
+SELECT pg_sequence_set_state('test_gapless', pg_sequence_get_state('test_gapless'));
+SELECT pg_sequence_get_state('test_gapless');
+
+-- check that event trigger works correctly
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+DROP SEQUENCE test_gapless;
+SELECT last_value FROM gapless_seq.seqam_gapless_values ORDER BY seqid;
+
+CREATE SEQUENCE test_gapless USING gapless;
+
+-- should fail due to deps
+DROP ACCESS METHOD gapless;
+-- likewise
+DROP EXTENSION gapless_seq;
+-- success
+DROP EXTENSION gapless_seq CASCADE;
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index c8708ec..0413e67 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -117,6 +117,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
  &earthdistance;
  &file-fdw;
  &fuzzystrmatch;
+ &gapless-seq;
  &hstore;
  &intagg;
  &intarray;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index e2bf725..0a4e728 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -121,6 +121,7 @@
 <!ENTITY earthdistance   SYSTEM "earthdistance.sgml">
 <!ENTITY file-fdw        SYSTEM "file-fdw.sgml">
 <!ENTITY fuzzystrmatch   SYSTEM "fuzzystrmatch.sgml">
+<!ENTITY gapless-seq     SYSTEM "gapless-seq.sgml">
 <!ENTITY hstore          SYSTEM "hstore.sgml">
 <!ENTITY intagg          SYSTEM "intagg.sgml">
 <!ENTITY intarray        SYSTEM "intarray.sgml">
diff --git a/doc/src/sgml/gapless-seq.sgml b/doc/src/sgml/gapless-seq.sgml
new file mode 100644
index 0000000..44cc224
--- /dev/null
+++ b/doc/src/sgml/gapless-seq.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/gapless_seq.sgml -->
+<sect1 id="gapless-seq" xreflabel="gapless_seq">
+ <title>gapless_seq</title>
+
+ <indexterm zone="gapless-seq">
+  <primary>gapless_seq</primary>
+ </indexterm>
+
+ <para>
+  <filename>gapless_seq</> provides a sequence implementation that never
+  has gaps in the sequence of values it produces, even after rollback or
+  a crash.
+ </para>
+
+ <para>
+  The consequence of this capability is that every nextval() request
+  writes a WAL record recording the latest state of the sequence and hold
+  lock on the sequence itself, so any concurrent nextval() calls have to wait
+  for the transaction to finish. This could be very costly and is not
+  recommended for general usage except in specific applications that require
+  this feature.
+ </para>
+
+</sect1>