seqam.v5.patch

application/octet-stream

Filename: seqam.v5.patch
Type: application/octet-stream
Part: 0
Message: Sequence Access Method WIP

Patch

Format: unified
Series: patch v5
File+
src/backend/access/common/reloptions.c 24 2
src/backend/access/Makefile 1 1
src/backend/access/sequence/Makefile 17 0
src/backend/access/sequence/seqam.c 278 0
src/backend/catalog/Makefile 1 1
src/backend/commands/sequence.c 117 15
src/backend/commands/tablecmds.c 3 0
src/backend/nodes/copyfuncs.c 4 0
src/backend/nodes/equalfuncs.c 4 0
src/backend/parser/gram.y 79 5
src/backend/parser/parse_utilcmd.c 2 2
src/backend/utils/cache/catcache.c 4 2
src/backend/utils/cache/syscache.c 23 0
src/backend/utils/misc/guc.c 12 0
src/include/access/reloptions.h 5 1
src/include/access/seqam.h 27 0
src/include/catalog/indexing.h 5 0
src/include/catalog/pg_proc.h 6 0
src/include/catalog/pg_seqam.h 70 0
src/include/nodes/parsenodes.h 6 2
src/include/utils/guc.h 1 0
src/include/utils/rel.h 15 7
src/include/utils/syscache.h 2 0
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,