From 443f70107bb0b8e4657558637d0cca761422337b Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 25 Apr 2024 17:15:20 +0900
Subject: [PATCH v7 5/8] Sequence access methods - backend support

The "seqlocal" sequence AM is now plugged in as a handler in the
relcache, and a set of callbacks in sequenceam.h.
---
 src/include/access/seqlocalam.h               |  15 --
 src/include/access/sequenceam.h               | 181 ++++++++++++++++++
 src/include/catalog/pg_am.dat                 |   3 +
 src/include/catalog/pg_am.h                   |   1 +
 src/include/catalog/pg_class.h                |   6 +
 src/include/catalog/pg_proc.dat               |  13 ++
 src/include/catalog/pg_type.dat               |   6 +
 src/include/commands/defrem.h                 |   1 +
 src/include/commands/sequence.h               |  34 ----
 src/include/nodes/meson.build                 |   1 +
 src/include/nodes/parsenodes.h                |   1 +
 src/include/utils/guc_hooks.h                 |   2 +
 src/include/utils/rel.h                       |   5 +
 src/backend/access/sequence/Makefile          |   2 +-
 src/backend/access/sequence/meson.build       |   1 +
 src/backend/access/sequence/seqlocalam.c      |  41 +++-
 src/backend/access/sequence/sequence.c        |   3 +-
 src/backend/access/sequence/sequenceamapi.c   | 145 ++++++++++++++
 src/backend/catalog/heap.c                    |   6 +-
 src/backend/commands/amcmds.c                 |  16 ++
 src/backend/commands/sequence.c               |  23 +--
 src/backend/commands/tablecmds.c              |  17 +-
 src/backend/nodes/Makefile                    |   1 +
 src/backend/nodes/gen_node_support.pl         |   2 +
 src/backend/parser/gram.y                     |  12 +-
 src/backend/parser/parse_utilcmd.c            |   2 +
 src/backend/utils/adt/pseudotypes.c           |   1 +
 src/backend/utils/cache/relcache.c            |  91 +++++++--
 src/backend/utils/misc/guc_tables.c           |  12 ++
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/bin/psql/describe.c                       |   2 +
 src/bin/psql/tab-complete.c                   |   4 +-
 src/test/regress/expected/create_am.out       |  55 ++++--
 src/test/regress/expected/opr_sanity.out      |  12 ++
 src/test/regress/expected/psql.out            |  96 +++++-----
 src/test/regress/sql/create_am.sql            |  24 ++-
 src/test/regress/sql/opr_sanity.sql           |  10 +
 src/tools/pgindent/typedefs.list              |   5 +-
 38 files changed, 683 insertions(+), 170 deletions(-)
 create mode 100644 src/include/access/sequenceam.h
 create mode 100644 src/backend/access/sequence/sequenceamapi.c

diff --git a/src/include/access/seqlocalam.h b/src/include/access/seqlocalam.h
index 225fb9a2cb..21936511ac 100644
--- a/src/include/access/seqlocalam.h
+++ b/src/include/access/seqlocalam.h
@@ -15,7 +15,6 @@
 
 #include "access/xlogreader.h"
 #include "storage/relfilelocator.h"
-#include "utils/rel.h"
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOCAL_LOG			0x00
@@ -41,18 +40,4 @@ extern void seq_local_desc(StringInfo buf, XLogReaderState *record);
 extern const char *seq_local_identify(uint8 info);
 extern void seq_local_mask(char *page, BlockNumber blkno);
 
-/* access routines */
-extern int64 seq_local_nextval(Relation rel, int64 incby, int64 maxv,
-							   int64 minv, int64 cache, bool cycle,
-							   int64 *last);
-extern const char *seq_local_get_table_am(void);
-extern void seq_local_init(Relation rel, int64 last_value, bool is_called);
-extern void seq_local_setval(Relation rel, int64 next, bool iscalled);
-extern void seq_local_reset(Relation rel, int64 startv, bool is_called,
-							bool reset_state);
-extern void seq_local_get_state(Relation rel, int64 *last_value,
-								bool *is_called);
-extern void seq_local_change_persistence(Relation rel,
-										 char newrelpersistence);
-
 #endif							/* SEQLOCALAM_H */
diff --git a/src/include/access/sequenceam.h b/src/include/access/sequenceam.h
new file mode 100644
index 0000000000..ac48c8b468
--- /dev/null
+++ b/src/include/access/sequenceam.h
@@ -0,0 +1,181 @@
+/*-------------------------------------------------------------------------
+ *
+ * sequenceam.h
+ *	  POSTGRES sequence access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/sequenceam.h
+ *
+ * NOTES
+ *		See sequenceam.sgml for higher level documentation.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQUENCEAM_H
+#define SEQUENCEAM_H
+
+#include "utils/rel.h"
+
+#define DEFAULT_SEQUENCE_ACCESS_METHOD "seqlocal"
+
+/* GUCs */
+extern PGDLLIMPORT char *default_sequence_access_method;
+
+/*
+ * API struct for a sequence AM.  Note this must be allocated in a
+ * server-lifetime manner, typically as a static const struct, which then gets
+ * returned by FormData_pg_am.amhandler.
+ *
+ * In most cases it's not appropriate to call the callbacks directly, use the
+ * sequence_* wrapper functions instead.
+ *
+ * GetSequenceAmRoutine() asserts that required callbacks are filled in,
+ * remember to update when adding a callback.
+ */
+typedef struct SequenceAmRoutine
+{
+	/* this must be set to T_SequenceAmRoutine */
+	NodeTag		type;
+
+	/*
+	 * Retrieve table access method used by a sequence to store its metadata.
+	 */
+	const char *(*get_table_am) (void);
+
+	/*
+	 * Initialize sequence after creating a sequence Relation in pg_class,
+	 * setting up the sequence for use.  "last_value" and "is_called" are
+	 * guessed from the options set for the sequence in CREATE SEQUENCE, based
+	 * on the configuration of pg_sequence.
+	 */
+	void		(*init) (Relation rel, int64 last_value, bool is_called);
+
+	/*
+	 * Retrieve a result for nextval(), based on the options retrieved from
+	 * the sequence's options in pg_sequence.  "last" is the last value
+	 * calculated stored in the session's local cache, for lastval().
+	 */
+	int64		(*nextval) (Relation rel, int64 incby, int64 maxv,
+							int64 minv, int64 cache, bool cycle,
+							int64 *last);
+
+	/*
+	 * Callback to set the state of a sequence, based on the input arguments
+	 * from setval().
+	 */
+	void		(*setval) (Relation rel, int64 next, bool iscalled);
+
+	/*
+	 * Reset a sequence to its initial value.  "reset_state", if set to true,
+	 * means that the sequence parameters have changed, hence its internal
+	 * state may need to be reset as well.  "startv" and "is_called" are
+	 * values guessed from the configuration of the sequence, based on the
+	 * contents of pg_sequence.
+	 */
+	void		(*reset) (Relation rel, int64 startv, bool is_called,
+						  bool reset_state);
+
+	/*
+	 * Returns the current state of a sequence, returning data for
+	 * pg_sequence_last_value() and related DDLs like ALTER SEQUENCE.
+	 * "last_value" and "is_called" should be assigned to the values retrieved
+	 * from the sequence Relation.
+	 */
+	void		(*get_state) (Relation rel, int64 *last_value, bool *is_called);
+
+	/*
+	 * Callback used when switching persistence of a sequence Relation, to
+	 * reset the sequence based on its new persistence "newrelpersistence".
+	 */
+	void		(*change_persistence) (Relation rel, char newrelpersistence);
+
+} SequenceAmRoutine;
+
+
+/* ---------------------------------------------------------------------------
+ * Wrapper functions for each callback.
+ * ---------------------------------------------------------------------------
+ */
+
+/*
+ * Returns the name of the table access method used by this sequence.
+ */
+static inline const char *
+sequence_get_table_am(Relation rel)
+{
+	return rel->rd_sequenceam->get_table_am();
+}
+
+/*
+ * Insert tuple data based on the information guessed from the contents
+ * of pg_sequence.
+ */
+static inline void
+sequence_init(Relation rel, int64 last_value, bool is_called)
+{
+	rel->rd_sequenceam->init(rel, last_value, is_called);
+}
+
+/*
+ * Allocate a set of values for the given sequence.  "last" is the last value
+ * allocated.  The result returned is the next value of the sequence computed.
+ */
+static inline int64
+sequence_nextval(Relation rel, int64 incby, int64 maxv,
+				 int64 minv, int64 cache, bool cycle,
+				 int64 *last)
+{
+	return rel->rd_sequenceam->nextval(rel, incby, maxv, minv, cache,
+									   cycle, last);
+}
+
+/*
+ * Callback to set the state of a sequence, based on the input arguments
+ * from setval().
+ */
+static inline void
+sequence_setval(Relation rel, int64 next, bool iscalled)
+{
+	rel->rd_sequenceam->setval(rel, next, iscalled);
+}
+
+/*
+ * Reset a sequence to its initial state.
+ */
+static inline void
+sequence_reset(Relation rel, int64 startv, bool is_called,
+			   bool reset_state)
+{
+	rel->rd_sequenceam->reset(rel, startv, is_called, reset_state);
+}
+
+/*
+ * Retrieve sequence metadata.
+ */
+static inline void
+sequence_get_state(Relation rel, int64 *last_value, bool *is_called)
+{
+	rel->rd_sequenceam->get_state(rel, last_value, is_called);
+}
+
+/*
+ * Callback to change the persistence of a sequence Relation.
+ */
+static inline void
+sequence_change_persistence(Relation rel, char newrelpersistence)
+{
+	rel->rd_sequenceam->change_persistence(rel, newrelpersistence);
+}
+
+/* ----------------------------------------------------------------------------
+ * Functions in sequenceamapi.c
+ * ----------------------------------------------------------------------------
+ */
+
+extern const SequenceAmRoutine *GetSequenceAmRoutine(Oid amhandler);
+extern Oid	GetSequenceAmRoutineId(Oid amoid);
+
+#endif							/* SEQUENCEAM_H */
diff --git a/src/include/catalog/pg_am.dat b/src/include/catalog/pg_am.dat
index db87490282..d991a77604 100644
--- a/src/include/catalog/pg_am.dat
+++ b/src/include/catalog/pg_am.dat
@@ -15,6 +15,9 @@
 { oid => '2', oid_symbol => 'HEAP_TABLE_AM_OID',
   descr => 'heap table access method',
   amname => 'heap', amhandler => 'heap_tableam_handler', amtype => 't' },
+{ oid => '8048', oid_symbol => 'LOCAL_SEQUENCE_AM_OID',
+  descr => 'local sequence access method',
+  amname => 'seqlocal', amhandler => 'seq_local_sequenceam_handler', amtype => 's' },
 { oid => '403', oid_symbol => 'BTREE_AM_OID',
   descr => 'b-tree index access method',
   amname => 'btree', amhandler => 'bthandler', amtype => 'i' },
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 475593fad4..3a94e8c636 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ MAKE_SYSCACHE(AMOID, pg_am_oid_index, 4);
  * Allowed values for amtype
  */
 #define AMTYPE_INDEX					'i' /* index access method */
+#define AMTYPE_SEQUENCE					's' /* sequence access method */
 #define AMTYPE_TABLE					't' /* table access method */
 
 #endif							/* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 0fc2c093b0..21623f34fa 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -228,6 +228,12 @@ MAKE_SYSCACHE(RELNAMENSP, pg_class_relname_nsp_index, 128);
 	 (relkind) == RELKIND_TOASTVALUE || \
 	 (relkind) == RELKIND_MATVIEW)
 
+/*
+ * Relation kinds with a sequence access method (rd_sequenceam).
+ */
+#define RELKIND_HAS_SEQUENCE_AM(relkind) \
+	((relkind) == RELKIND_SEQUENCE)
+
 extern int	errdetail_relkind_not_supported(char relkind);
 
 #endif							/* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 986df08c6e..7c4388e000 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -913,6 +913,12 @@
   prorettype => 'table_am_handler', proargtypes => 'internal',
   prosrc => 'heap_tableam_handler' },
 
+# Sequence access method handlers
+{ oid => '8209', descr => 'local sequence access method handler',
+  proname => 'seq_local_sequenceam_handler', provolatile => 'v',
+  prorettype => 'sequence_am_handler', proargtypes => 'internal',
+  prosrc => 'seq_local_sequenceam_handler' },
+
 # Index access method handlers
 { oid => '330', descr => 'btree index access method handler',
   proname => 'bthandler', provolatile => 'v', prorettype => 'index_am_handler',
@@ -7677,6 +7683,13 @@
 { oid => '327', descr => 'I/O',
   proname => 'index_am_handler_out', prorettype => 'cstring',
   proargtypes => 'index_am_handler', prosrc => 'index_am_handler_out' },
+{ oid => '8207', descr => 'I/O',
+  proname => 'sequence_am_handler_in', proisstrict => 'f',
+  prorettype => 'sequence_am_handler', proargtypes => 'cstring',
+  prosrc => 'sequence_am_handler_in' },
+{ oid => '8208', descr => 'I/O',
+  proname => 'sequence_am_handler_out', prorettype => 'cstring',
+  proargtypes => 'sequence_am_handler', prosrc => 'sequence_am_handler_out' },
 { oid => '3311', descr => 'I/O',
   proname => 'tsm_handler_in', proisstrict => 'f', prorettype => 'tsm_handler',
   proargtypes => 'cstring', prosrc => 'tsm_handler_in' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index ceff66ccde..0eba013687 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -627,6 +627,12 @@
   typcategory => 'P', typinput => 'index_am_handler_in',
   typoutput => 'index_am_handler_out', typreceive => '-', typsend => '-',
   typalign => 'i' },
+{ oid => '8210',
+  descr => 'pseudo-type for the result of a sequence AM handler function',
+  typname => 'sequence_am_handler', typlen => '4', typbyval => 't',
+  typtype => 'p', typcategory => 'P', typinput => 'sequence_am_handler_in',
+  typoutput => 'sequence_am_handler_out', typreceive => '-', typsend => '-',
+  typalign => 'i' },
 { oid => '3310',
   descr => 'pseudo-type for the result of a tablesample method function',
   typname => 'tsm_handler', typlen => '4', typbyval => 't', typtype => 'p',
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 29c511e319..39d2cae422 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -141,6 +141,7 @@ extern Datum transformGenericOptions(Oid catalogId,
 extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
 extern Oid	get_index_am_oid(const char *amname, bool missing_ok);
 extern Oid	get_table_am_oid(const char *amname, bool missing_ok);
+extern Oid	get_sequence_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 e88cbee3b5..9b22b2f248 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -22,35 +22,6 @@
 #include "storage/relfilelocator.h"
 
 
-typedef struct FormData_pg_sequence_data
-{
-	int64		last_value;
-	int64		log_cnt;
-	bool		is_called;
-} FormData_pg_sequence_data;
-
-typedef FormData_pg_sequence_data *Form_pg_sequence_data;
-
-/*
- * Columns of a sequence relation
- */
-
-#define SEQ_COL_LASTVAL			1
-#define SEQ_COL_LOG				2
-#define SEQ_COL_CALLED			3
-
-#define SEQ_COL_FIRSTCOL		SEQ_COL_LASTVAL
-#define SEQ_COL_LASTCOL			SEQ_COL_CALLED
-
-/* XLOG stuff */
-#define XLOG_SEQ_LOG			0x00
-
-typedef struct xl_seq_rec
-{
-	RelFileLocator locator;
-	/* SEQUENCE TUPLE DATA FOLLOWS AT THE END */
-} xl_seq_rec;
-
 extern int64 nextval_internal(Oid relid, bool check_permissions);
 extern Datum nextval(PG_FUNCTION_ARGS);
 extern List *sequence_options(Oid relid);
@@ -62,9 +33,4 @@ extern void DeleteSequenceTuple(Oid relid);
 extern void ResetSequence(Oid seq_relid);
 extern void ResetSequenceCaches(void);
 
-extern void seq_redo(XLogReaderState *record);
-extern void seq_desc(StringInfo buf, XLogReaderState *record);
-extern const char *seq_identify(uint8 info);
-extern void seq_mask(char *page, BlockNumber blkno);
-
 #endif							/* SEQUENCE_H */
diff --git a/src/include/nodes/meson.build b/src/include/nodes/meson.build
index b665e55b65..774ae2652c 100644
--- a/src/include/nodes/meson.build
+++ b/src/include/nodes/meson.build
@@ -9,6 +9,7 @@ node_support_input_i = [
   'nodes/execnodes.h',
   'access/amapi.h',
   'access/sdir.h',
+  'access/sequenceam.h',
   'access/tableam.h',
   'access/tsmapi.h',
   'commands/event_trigger.h',
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f5fe0d2257..6dc56e3eaf 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3112,6 +3112,7 @@ typedef struct CreateSeqStmt
 	List	   *options;
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
 	bool		for_identity;
+	char	   *accessMethod;	/* USING name of access method (eg. local) */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateSeqStmt;
 
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 5813dba0a2..f4a49969b8 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -53,6 +53,8 @@ extern bool check_debug_io_direct(char **newval, void **extra, GucSource source)
 extern void assign_debug_io_direct(const char *newval, void *extra);
 extern bool check_default_table_access_method(char **newval, void **extra,
 											  GucSource source);
+extern bool check_default_sequence_access_method(char **newval, void **extra,
+												 GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra,
 									 GucSource source);
 extern bool check_default_text_search_config(char **newval, void **extra, GucSource source);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 8700204953..2a4b7bed2b 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -188,6 +188,11 @@ typedef struct RelationData
 	 */
 	const struct TableAmRoutine *rd_tableam;
 
+	/*
+	 * Sequence access method.
+	 */
+	const struct SequenceAmRoutine *rd_sequenceam;
+
 	/* 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: */
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
index a15ceec1c0..62006165a1 100644
--- a/src/backend/access/sequence/Makefile
+++ b/src/backend/access/sequence/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/sequence
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = seqlocalam.o seqlocalxlog.o sequence.o
+OBJS = seqlocalam.o seqlocalxlog.o sequence.o sequenceamapi.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/meson.build b/src/backend/access/sequence/meson.build
index 4d24f900b6..ea91990552 100644
--- a/src/backend/access/sequence/meson.build
+++ b/src/backend/access/sequence/meson.build
@@ -4,4 +4,5 @@ backend_sources += files(
   'seqlocalam.c',
   'seqlocalxlog.c',
   'sequence.c',
+  'sequenceamapi.c',
 )
diff --git a/src/backend/access/sequence/seqlocalam.c b/src/backend/access/sequence/seqlocalam.c
index b0a0d72966..2e524c9e99 100644
--- a/src/backend/access/sequence/seqlocalam.c
+++ b/src/backend/access/sequence/seqlocalam.c
@@ -17,6 +17,7 @@
 
 #include "access/multixact.h"
 #include "access/seqlocalam.h"
+#include "access/sequenceam.h"
 #include "access/xact.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
@@ -24,6 +25,7 @@
 #include "commands/tablecmds.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
+#include "utils/builtins.h"
 
 
 /*
@@ -230,10 +232,10 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
  * Allocate a new value for a local sequence, based on the sequence
  * configuration.
  */
-int64
+static int64
 seq_local_nextval(Relation rel, int64 incby, int64 maxv,
-				  int64 minv, int64 cache, bool cycle,
-				  int64 *last)
+			  int64 minv, int64 cache, bool cycle,
+			  int64 *last)
 {
 	int64		result;
 	int64		fetch;
@@ -417,7 +419,7 @@ seq_local_nextval(Relation rel, int64 incby, int64 maxv,
  *
  * Return the table access method used by this sequence.
  */
-const char *
+static const char *
 seq_local_get_table_am(void)
 {
 	return "heap";
@@ -432,7 +434,7 @@ seq_local_get_table_am(void)
  * inserted after the relation has been created, filling in its heap
  * table.
  */
-void
+static void
 seq_local_init(Relation rel, int64 last_value, bool is_called)
 {
 	Datum		value[SEQ_LOCAL_COL_LASTCOL];
@@ -499,7 +501,7 @@ seq_local_init(Relation rel, int64 last_value, bool is_called)
  *
  * Callback for setval().
  */
-void
+static void
 seq_local_setval(Relation rel, int64 next, bool iscalled)
 {
 	Buffer		buf;
@@ -547,7 +549,7 @@ seq_local_setval(Relation rel, int64 next, bool iscalled)
  * Perform a hard reset on the local sequence, rewriting its heap data
  * entirely.
  */
-void
+static void
 seq_local_reset(Relation rel, int64 startv, bool is_called, bool reset_state)
 {
 	Form_pg_seq_local_data seq;
@@ -600,7 +602,7 @@ seq_local_reset(Relation rel, int64 startv, bool is_called, bool reset_state)
  *
  * Retrieve the state of a local sequence.
  */
-void
+static void
 seq_local_get_state(Relation rel, int64 *last_value, bool *is_called)
 {
 	Buffer		buf;
@@ -621,7 +623,7 @@ seq_local_get_state(Relation rel, int64 *last_value, bool *is_called)
  *
  * Persistence change for the local sequence Relation.
  */
-void
+static void
 seq_local_change_persistence(Relation rel, char newrelpersistence)
 {
 	Buffer		buf;
@@ -632,3 +634,24 @@ seq_local_change_persistence(Relation rel, char newrelpersistence)
 	fill_seq_with_data(rel, &seqdatatuple);
 	UnlockReleaseBuffer(buf);
 }
+
+/* ------------------------------------------------------------------------
+ * Definition of the local sequence access method.
+ * ------------------------------------------------------------------------
+ */
+static const SequenceAmRoutine seq_local_methods = {
+	.type = T_SequenceAmRoutine,
+	.get_table_am = seq_local_get_table_am,
+	.init = seq_local_init,
+	.nextval = seq_local_nextval,
+	.setval = seq_local_setval,
+	.reset = seq_local_reset,
+	.get_state = seq_local_get_state,
+	.change_persistence = seq_local_change_persistence
+};
+
+Datum
+seq_local_sequenceam_handler(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_POINTER(&seq_local_methods);
+}
diff --git a/src/backend/access/sequence/sequence.c b/src/backend/access/sequence/sequence.c
index 8d6b7bb5dc..5e7d76d3b0 100644
--- a/src/backend/access/sequence/sequence.c
+++ b/src/backend/access/sequence/sequence.c
@@ -13,7 +13,8 @@
  *
  * NOTES
  *	  This file contains sequence_ routines that implement access to sequences
- *	  (in contrast to other relation types like indexes).
+ *	  (in contrast to other relation types like indexes) that are independent
+ *	  of individual sequence access methods.
  *
  *-------------------------------------------------------------------------
  */
diff --git a/src/backend/access/sequence/sequenceamapi.c b/src/backend/access/sequence/sequenceamapi.c
new file mode 100644
index 0000000000..dd1a60d827
--- /dev/null
+++ b/src/backend/access/sequence/sequenceamapi.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * sequenceamapi.c
+ *	  general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/sequence/sequenceamapi.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.
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/sequenceam.h"
+#include "catalog/pg_am.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/guc_hooks.h"
+#include "utils/syscache.h"
+
+
+/* GUC */
+char	   *default_sequence_access_method = DEFAULT_SEQUENCE_ACCESS_METHOD;
+
+/*
+ * GetSequenceAmRoutine
+ *		Call the specified access method handler routine to get its
+ *		SequenceAmRoutine struct, which will be palloc'd in the caller's
+ *		memory context.
+ */
+const SequenceAmRoutine *
+GetSequenceAmRoutine(Oid amhandler)
+{
+	Datum		datum;
+	SequenceAmRoutine *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (SequenceAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, SequenceAmRoutine))
+		elog(ERROR, "sequence access method handler %u did not return a SequenceAmRoutine struct",
+			 amhandler);
+
+	/*
+	 * Assert that all required callbacks are present.  That makes it a bit
+	 * easier to keep AMs up to date, e.g. when forward porting them to a new
+	 * major version.
+	 */
+	Assert(routine->get_table_am != NULL);
+	Assert(routine->init != NULL);
+	Assert(routine->nextval != NULL);
+	Assert(routine->setval != NULL);
+	Assert(routine->reset != NULL);
+	Assert(routine->get_state != NULL);
+	Assert(routine->change_persistence != NULL);
+
+	return routine;
+}
+
+/*
+ * GetSequenceAmRoutineId
+ *		Call pg_am and retrieve the OID of the access method handler.
+ */
+Oid
+GetSequenceAmRoutineId(Oid amoid)
+{
+	Oid			amhandleroid;
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+
+	tuple = SearchSysCache1(AMOID,
+							ObjectIdGetDatum(amoid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u", amoid);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	Assert(aform->amtype == AMTYPE_SEQUENCE);
+	amhandleroid = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	return amhandleroid;
+}
+
+/* check_hook: validate new default_sequence_access_method */
+bool
+check_default_sequence_access_method(char **newval, void **extra,
+									 GucSource source)
+{
+	if (**newval == '\0')
+	{
+		GUC_check_errdetail("%s cannot be empty.",
+							"default_sequence_access_method");
+		return false;
+	}
+
+	if (strlen(*newval) >= NAMEDATALEN)
+	{
+		GUC_check_errdetail("%s is too long (maximum %d characters).",
+							"default_sequence_access_method", NAMEDATALEN - 1);
+		return false;
+	}
+
+	/*
+	 * If we aren't inside a transaction, or not connected to a database, we
+	 * cannot do the catalog access necessary to verify the method.  Must
+	 * accept the value on faith.
+	 */
+	if (IsTransactionState() && MyDatabaseId != InvalidOid)
+	{
+		if (!OidIsValid(get_sequence_am_oid(*newval, true)))
+		{
+			/*
+			 * When source == PGC_S_TEST, don't throw a hard error for a
+			 * nonexistent sequence access method, only a NOTICE. See comments
+			 * in guc.h.
+			 */
+			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;
+}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 01b43cc6a8..3b8d5a9989 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1472,8 +1472,12 @@ heap_create_with_catalog(const char *relname,
 		 *
 		 * No need to add an explicit dependency for the toast table, as the
 		 * main table depends on it.
+		 *
+		 * Sequences and tables are created with their access method ID
+		 * given by the caller of this function.
 		 */
-		if (RELKIND_HAS_TABLE_AM(relkind) && relkind != RELKIND_TOASTVALUE)
+		if ((RELKIND_HAS_TABLE_AM(relkind) && relkind != RELKIND_TOASTVALUE) ||
+			RELKIND_HAS_SEQUENCE_AM(relkind))
 		{
 			ObjectAddressSet(referenced, AccessMethodRelationId, accessmtd);
 			add_exact_object_address(&referenced, addrs);
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index aaa0f9a1dc..1a27f191a3 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -15,6 +15,7 @@
 
 #include "access/htup_details.h"
 #include "access/table.h"
+#include "access/sequenceam.h"
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
@@ -175,6 +176,16 @@ get_table_am_oid(const char *amname, bool missing_ok)
 	return get_am_type_oid(amname, AMTYPE_TABLE, missing_ok);
 }
 
+/*
+ * get_sequence_am_oid - given an access method name, look up its OID
+ *		and verify it corresponds to an sequence AM.
+ */
+Oid
+get_sequence_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.
@@ -215,6 +226,8 @@ get_am_type_string(char amtype)
 	{
 		case AMTYPE_INDEX:
 			return "INDEX";
+		case AMTYPE_SEQUENCE:
+			return "SEQUENCE";
 		case AMTYPE_TABLE:
 			return "TABLE";
 		default:
@@ -251,6 +264,9 @@ lookup_am_handler_func(List *handler_name, char amtype)
 		case AMTYPE_INDEX:
 			expectedType = INDEX_AM_HANDLEROID;
 			break;
+		case AMTYPE_SEQUENCE:
+			expectedType = SEQUENCE_AM_HANDLEROID;
+			break;
 		case AMTYPE_TABLE:
 			expectedType = TABLE_AM_HANDLEROID;
 			break;
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index e75878d8c6..1baea307ed 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -16,10 +16,10 @@
 
 #include "access/bufmask.h"
 #include "access/htup_details.h"
-#include "access/seqlocalam.h"
 #include "access/multixact.h"
 #include "access/relation.h"
 #include "access/sequence.h"
+#include "access/sequenceam.h"
 #include "access/table.h"
 #include "access/transam.h"
 #include "access/xact.h"
@@ -152,6 +152,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
 	stmt->inhRelations = NIL;
 	stmt->constraints = NIL;
 	stmt->options = NIL;
+	stmt->accessMethod = seq->accessMethod ? pstrdup(seq->accessMethod) : NULL;
 	stmt->oncommit = ONCOMMIT_NOOP;
 	stmt->tablespacename = NULL;
 	stmt->if_not_exists = seq->if_not_exists;
@@ -173,7 +174,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
 	rel = sequence_open(seqoid, AccessExclusiveLock);
 
 	/* now initialize the sequence table structure and its data */
-	seq_local_init(rel, last_value, is_called);
+	sequence_init(rel, last_value, is_called);
 
 	/* process OWNED BY if given */
 	if (owned_by)
@@ -241,7 +242,7 @@ ResetSequence(Oid seq_relid)
 	ReleaseSysCache(pgstuple);
 
 	/* Sequence state is forcibly reset here. */
-	seq_local_reset(seq_rel, startv, false, true);
+	sequence_reset(seq_rel, startv, false, true);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
@@ -297,7 +298,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
 	seqform = (Form_pg_sequence) GETSTRUCT(seqtuple);
 
 	/* Read sequence data */
-	seq_local_get_state(seqrel, &last_value, &is_called);
+	sequence_get_state(seqrel, &last_value, &is_called);
 
 	/* Check and set new values */
 	init_params(pstate, stmt->options, stmt->for_identity, false,
@@ -310,7 +311,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
 		if (RelationNeedsWAL(seqrel))
 			GetTopTransactionId();
 
-		seq_local_reset(seqrel, last_value, is_called, reset_state);
+		sequence_reset(seqrel, last_value, is_called, reset_state);
 	}
 
 	/* Clear local cache so that we don't think we have cached numbers */
@@ -353,7 +354,7 @@ SequenceChangePersistence(Oid relid, char newrelpersistence)
 	if (RelationNeedsWAL(seqrel))
 		GetTopTransactionId();
 
-	seq_local_change_persistence(seqrel, newrelpersistence);
+	sequence_change_persistence(seqrel, newrelpersistence);
 
 	sequence_close(seqrel, NoLock);
 }
@@ -470,8 +471,8 @@ nextval_internal(Oid relid, bool check_permissions)
 	ReleaseSysCache(pgstuple);
 
 	/* retrieve next value from the access method */
-	result = seq_local_nextval(seqrel, incby, maxv, minv, cache, cycle,
-							   &last);
+	result = sequence_nextval(seqrel, incby, maxv, minv, cache, cycle,
+							  &last);
 
 	/* save info in local cache */
 	elm->increment = incby;
@@ -625,7 +626,7 @@ do_setval(Oid relid, int64 next, bool iscalled)
 		GetTopTransactionId();
 
 	/* Call the access method callback */
-	seq_local_setval(seqrel, next, iscalled);
+	sequence_setval(seqrel, next, iscalled);
 
 	sequence_close(seqrel, NoLock);
 }
@@ -1350,7 +1351,7 @@ pg_sequence_read_tuple(PG_FUNCTION_ARGS)
 		bool		is_called;
 		int64		last_value;
 
-		seq_local_get_state(seqrel, &last_value, &is_called);
+		sequence_get_state(seqrel, &last_value, &is_called);
 
 		values[0] = Int64GetDatum(last_value);
 		values[1] = BoolGetDatum(is_called);
@@ -1397,7 +1398,7 @@ pg_sequence_last_value(PG_FUNCTION_ARGS)
 		!RELATION_IS_OTHER_TEMP(seqrel) &&
 		(RelationIsPermanent(seqrel) || !RecoveryInProgress()))
 	{
-		seq_local_get_state(seqrel, &result, &is_called);
+		sequence_get_state(seqrel, &result, &is_called);
 	}
 
 	sequence_close(seqrel, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f6e3873b29..5541948736 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -22,6 +22,7 @@
 #include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/sysattr.h"
+#include "access/sequenceam.h"
 #include "access/tableam.h"
 #include "access/toast_compression.h"
 #include "access/xact.h"
@@ -942,14 +943,18 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	}
 
 	/*
-	 * For relations with table AM and partitioned tables, select access
-	 * method to use: an explicitly indicated one, or (in the case of a
+	 * For relations with table AM, partitioned tables or sequences, select
+	 * access method to use: an explicitly indicated one, or (in the case of a
 	 * partitioned table) the parent's, if it has one.
 	 */
 	if (stmt->accessMethod != NULL)
 	{
-		Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
-		accessMethodId = get_table_am_oid(stmt->accessMethod, false);
+		Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE ||
+			   RELKIND_HAS_SEQUENCE_AM(relkind));
+		if (RELKIND_HAS_SEQUENCE_AM(relkind))
+			accessMethodId = get_sequence_am_oid(stmt->accessMethod, false);
+		else
+			accessMethodId = get_table_am_oid(stmt->accessMethod, false);
 	}
 	else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
 	{
@@ -962,6 +967,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 		if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
 			accessMethodId = get_table_am_oid(default_table_access_method, false);
 	}
+	else if (RELKIND_HAS_SEQUENCE_AM(relkind))
+	{
+		accessMethodId = get_sequence_am_oid(default_sequence_access_method, false);
+	}
 
 	/*
 	 * Create the relation.  Inherited defaults and constraints are passed in
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 66bbad8e6e..86b1773c0b 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -47,6 +47,7 @@ node_headers = \
 	nodes/execnodes.h \
 	access/amapi.h \
 	access/sdir.h \
+	access/sequenceam.h \
 	access/tableam.h \
 	access/tsmapi.h \
 	commands/event_trigger.h \
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 81df3bdf95..d2450e559d 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -59,6 +59,7 @@ my @all_input_files = qw(
   nodes/execnodes.h
   access/amapi.h
   access/sdir.h
+  access/sequenceam.h
   access/tableam.h
   access/tsmapi.h
   commands/event_trigger.h
@@ -83,6 +84,7 @@ my @nodetag_only_files = qw(
   nodes/execnodes.h
   access/amapi.h
   access/sdir.h
+  access/sequenceam.h
   access/tableam.h
   access/tsmapi.h
   commands/event_trigger.h
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 84cef57a70..8e376d974a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -390,6 +390,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <str>		copy_file_name
 				access_method_clause attr_name
 				table_access_method_clause name cursor_name file_name
+				sequence_access_method_clause
 				cluster_index_specification
 
 %type <list>	func_name handler_name qual_Op qual_all_Op subquery_Op
@@ -4831,23 +4832,26 @@ RefreshMatViewStmt:
 
 CreateSeqStmt:
 			CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+				sequence_access_method_clause
 				{
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
-
 					$4->relpersistence = $2;
 					n->sequence = $4;
 					n->options = $5;
+					n->accessMethod = $6;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = false;
 					$$ = (Node *) n;
 				}
 			| CREATE OptTemp SEQUENCE IF_P NOT EXISTS qualified_name OptSeqOptList
+				sequence_access_method_clause
 				{
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
 
 					$7->relpersistence = $2;
 					n->sequence = $7;
 					n->options = $8;
+					n->accessMethod = $9;
 					n->ownerId = InvalidOid;
 					n->if_not_exists = true;
 					$$ = (Node *) n;
@@ -4884,6 +4888,11 @@ OptParenthesizedSeqOptList: '(' SeqOptList ')'		{ $$ = $2; }
 			| /*EMPTY*/								{ $$ = NIL; }
 		;
 
+sequence_access_method_clause:
+			USING name							{ $$ = $2; }
+			| /*EMPTY*/							{ $$ = NULL; }
+		;
+
 SeqOptList: SeqOptElem								{ $$ = list_make1($1); }
 			| SeqOptList SeqOptElem					{ $$ = lappend($1, $2); }
 		;
@@ -5880,6 +5889,7 @@ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
 
 am_type:
 			INDEX			{ $$ = AMTYPE_INDEX; }
+		|	SEQUENCE		{ $$ = AMTYPE_SEQUENCE; }
 		|	TABLE			{ $$ = AMTYPE_TABLE; }
 		;
 
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 79cad4ab30..e2fd33a530 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -26,6 +26,7 @@
 #include "access/htup_details.h"
 #include "access/relation.h"
 #include "access/reloptions.h"
+#include "access/sequenceam.h"
 #include "access/table.h"
 #include "access/toast_compression.h"
 #include "catalog/dependency.h"
@@ -461,6 +462,7 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
 	seqstmt->sequence->relpersistence = cxt->rel ? cxt->rel->rd_rel->relpersistence : cxt->relation->relpersistence;
 
 	seqstmt->options = seqoptions;
+	seqstmt->accessMethod = NULL;
 
 	/*
 	 * If a sequence data type was specified, add it to the options.  Prepend
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index e189e9b79d..e31dbb0ebb 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -369,6 +369,7 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(language_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(fdw_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(index_am_handler);
+PSEUDOTYPE_DUMMY_IO_FUNCS(sequence_am_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 66ed24e401..6f80d4f707 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -34,6 +34,7 @@
 #include "access/multixact.h"
 #include "access/parallel.h"
 #include "access/reloptions.h"
+#include "access/sequenceam.h"
 #include "access/sysattr.h"
 #include "access/table.h"
 #include "access/tableam.h"
@@ -64,6 +65,7 @@
 #include "catalog/pg_type.h"
 #include "catalog/schemapg.h"
 #include "catalog/storage.h"
+#include "commands/defrem.h"
 #include "commands/policy.h"
 #include "commands/publicationcmds.h"
 #include "commands/trigger.h"
@@ -301,6 +303,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 RelationInitSequenceAccessMethod(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1204,8 +1207,7 @@ retry:
 	if (relation->rd_rel->relkind == RELKIND_INDEX ||
 		relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
 		RelationInitIndexAccessInfo(relation);
-	else if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind) ||
-			 relation->rd_rel->relkind == RELKIND_SEQUENCE)
+	else if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
 		RelationInitTableAccessMethod(relation);
 	else if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
@@ -1214,6 +1216,8 @@ retry:
 		 * inherit.
 		 */
 	}
+	else if (RELKIND_HAS_SEQUENCE_AM(relation->rd_rel->relkind))
+		RelationInitSequenceAccessMethod(relation);
 	else
 		Assert(relation->rd_rel->relam == InvalidOid);
 
@@ -1810,17 +1814,9 @@ RelationInitTableAccessMethod(Relation relation)
 	HeapTuple	tuple;
 	Form_pg_am	aform;
 
-	if (relation->rd_rel->relkind == RELKIND_SEQUENCE)
-	{
-		/*
-		 * Sequences are currently accessed like heap tables, but it doesn't
-		 * seem prudent to show that in the catalog. So just overwrite it
-		 * here.
-		 */
-		Assert(relation->rd_rel->relam == InvalidOid);
-		relation->rd_amhandler = F_HEAP_TABLEAM_HANDLER;
-	}
-	else if (IsCatalogRelation(relation))
+	Assert(RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind));
+
+	if (IsCatalogRelation(relation))
 	{
 		/*
 		 * Avoid doing a syscache lookup for catalog tables.
@@ -1851,6 +1847,49 @@ RelationInitTableAccessMethod(Relation relation)
 	InitTableAmRoutine(relation);
 }
 
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessMethod(Relation relation)
+{
+	HeapTuple	tuple;
+	Form_pg_am	aform;
+	const char *tableam_name;
+	Oid			tableam_oid;
+	Oid			tableam_handler;
+
+	Assert(RELKIND_HAS_SEQUENCE_AM(relation->rd_rel->relkind));
+
+	/*
+	 * Look up the sequence access method, save the OID of its handler
+	 * function.
+	 */
+	Assert(relation->rd_rel->relam != InvalidOid);
+	relation->rd_amhandler = GetSequenceAmRoutineId(relation->rd_rel->relam);
+
+	/*
+	 * Now we can fetch the sequence AM's API struct.
+	 */
+	relation->rd_sequenceam = GetSequenceAmRoutine(relation->rd_amhandler);
+
+	/*
+	 * From the sequence AM, set its expected table access method.
+	 */
+	tableam_name = sequence_get_table_am(relation);
+	tableam_oid = get_table_am_oid(tableam_name, false);
+
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(tableam_oid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 tableam_oid);
+	aform = (Form_pg_am) GETSTRUCT(tuple);
+	tableam_handler = aform->amhandler;
+	ReleaseSysCache(tuple);
+
+	relation->rd_tableam = GetTableAmRoutine(tableam_handler);
+}
+
 /*
  *		formrdesc
  *
@@ -3711,14 +3750,17 @@ RelationBuildLocalRelation(const char *relname,
 	rel->rd_rel->relam = accessmtd;
 
 	/*
-	 * RelationInitTableAccessMethod will do syscache lookups, so we mustn't
-	 * run it in CacheMemoryContext.  Fortunately, the remaining steps don't
-	 * require a long-lived current context.
+	 * RelationInitTableAccessMethod() and RelationInitSequenceAccessMethod()
+	 * will do syscache lookups, so we mustn't run them in CacheMemoryContext.
+	 * Fortunately, the remaining steps don't require a long-lived current
+	 * context.
 	 */
 	MemoryContextSwitchTo(oldcxt);
 
-	if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_SEQUENCE)
+	if (RELKIND_HAS_TABLE_AM(relkind))
 		RelationInitTableAccessMethod(rel);
+	else if (relkind == RELKIND_SEQUENCE)
+		RelationInitSequenceAccessMethod(rel);
 
 	/*
 	 * Okay to insert into the relcache hash table.
@@ -4333,13 +4375,21 @@ RelationCacheInitializePhase3(void)
 
 		/* Reload tableam data if needed */
 		if (relation->rd_tableam == NULL &&
-			(RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind) || relation->rd_rel->relkind == RELKIND_SEQUENCE))
+			(RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind)))
 		{
 			RelationInitTableAccessMethod(relation);
 			Assert(relation->rd_tableam != NULL);
 
 			restart = true;
 		}
+		else if (relation->rd_sequenceam == NULL &&
+				 relation->rd_rel->relkind == RELKIND_SEQUENCE)
+		{
+			RelationInitSequenceAccessMethod(relation);
+			Assert(relation->rd_sequenceam != NULL);
+
+			restart = true;
+		}
 
 		/* Release hold on the relation */
 		RelationDecrementReferenceCount(relation);
@@ -6326,8 +6376,10 @@ load_relcache_init_file(bool shared)
 				nailed_rels++;
 
 			/* Load table AM data */
-			if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) || rel->rd_rel->relkind == RELKIND_SEQUENCE)
+			if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind))
 				RelationInitTableAccessMethod(rel);
+			else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+				RelationInitSequenceAccessMethod(rel);
 
 			Assert(rel->rd_index == NULL);
 			Assert(rel->rd_indextuple == NULL);
@@ -6339,6 +6391,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_sequenceam == NULL);
 			Assert(rel->rd_opcoptions == NULL);
 		}
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index af227b1f24..ef768e3437 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -30,6 +30,7 @@
 #include "access/gin.h"
 #include "access/slru.h"
 #include "access/toast_compression.h"
+#include "access/sequenceam.h"
 #include "access/twophase.h"
 #include "access/xlog_internal.h"
 #include "access/xlogprefetcher.h"
@@ -4185,6 +4186,17 @@ struct config_string ConfigureNamesString[] =
 		check_default_table_access_method, NULL, NULL
 	},
 
+	{
+		{"default_sequence_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the default sequence access method for new sequences."),
+			NULL,
+			GUC_IS_NAME
+		},
+		&default_sequence_access_method,
+		DEFAULT_SEQUENCE_ACCESS_METHOD,
+		check_default_sequence_access_method, NULL, NULL
+	},
+
 	{
 		{"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the default tablespace to create tables and indexes in."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 667e0dc40a..879c03c770 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -703,6 +703,7 @@
 					#   error
 #search_path = '"$user", public'	# schema names
 #row_security = on
+#default_sequence_access_method = 'seqlocal'
 #default_table_access_method = 'heap'
 #default_tablespace = ''		# a tablespace name, '' uses the default
 #default_toast_compression = 'pglz'	# 'pglz' or 'lz4'
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 7c9a1f234c..98ff40f00d 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -161,10 +161,12 @@ describeAccessMethods(const char *pattern, bool verbose)
 					  "SELECT amname AS \"%s\",\n"
 					  "  CASE amtype"
 					  " WHEN 'i' THEN '%s'"
+					  " WHEN 's' THEN '%s'"
 					  " WHEN 't' THEN '%s'"
 					  " END AS \"%s\"",
 					  gettext_noop("Name"),
 					  gettext_noop("Index"),
+					  gettext_noop("Sequence"),
 					  gettext_noop("Table"),
 					  gettext_noop("Type"));
 
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index a7ccde6d7d..0352c73e30 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2200,7 +2200,7 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches("ALTER", "SEQUENCE", MatchAny))
 		COMPLETE_WITH("AS", "INCREMENT", "MINVALUE", "MAXVALUE", "RESTART",
 					  "START", "NO", "CACHE", "CYCLE", "SET", "OWNED BY",
-					  "OWNER TO", "RENAME TO");
+					  "OWNER TO", "RENAME TO", "USING");
 	/* ALTER SEQUENCE <name> AS */
 	else if (TailMatches("ALTER", "SEQUENCE", MatchAny, "AS"))
 		COMPLETE_WITH_CS("smallint", "integer", "bigint");
@@ -3226,7 +3226,7 @@ psql_completion(const char *text, int start, int end)
 	else if (TailMatches("CREATE", "SEQUENCE", MatchAny) ||
 			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny))
 		COMPLETE_WITH("AS", "INCREMENT BY", "MINVALUE", "MAXVALUE", "NO",
-					  "CACHE", "CYCLE", "OWNED BY", "START WITH");
+					  "CACHE", "CYCLE", "OWNED BY", "START WITH", "USING");
 	else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "AS") ||
 			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "AS"))
 		COMPLETE_WITH_CS("smallint", "integer", "bigint");
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index 35d4cf1d46..f1ad1c583f 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -163,11 +163,6 @@ CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
 ERROR:  syntax error at or near "USING"
 LINE 1: CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM ...
                                        ^
--- CREATE SEQUENCE doesn't support USING
-CREATE SEQUENCE tableam_seq_heap2 USING heap2;
-ERROR:  syntax error at or near "USING"
-LINE 1: CREATE SEQUENCE tableam_seq_heap2 USING heap2;
-                                          ^
 -- CREATE MATERIALIZED VIEW does support USING
 CREATE MATERIALIZED VIEW tableam_tblmv_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
 SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1;
@@ -500,9 +495,12 @@ CREATE TABLE tableam_parted_heapx (a text, b int) PARTITION BY list (a);
 CREATE TABLE tableam_parted_1_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('a', 'b');
 -- but an explicitly set AM overrides it
 CREATE TABLE tableam_parted_2_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('c', 'd') USING heap;
--- sequences, views and foreign servers shouldn't have an AM
-CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx;
+-- sequences have an AM
+SET LOCAL default_sequence_access_method = 'seqlocal';
 CREATE SEQUENCE tableam_seq_heapx;
+RESET default_sequence_access_method;
+-- views and foreign servers shouldn't have an AM
+CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx;
 CREATE FOREIGN DATA WRAPPER fdw_heap2 VALIDATOR postgresql_fdw_validator;
 CREATE SERVER fs_heap2 FOREIGN DATA WRAPPER fdw_heap2 ;
 CREATE FOREIGN table tableam_fdw_heapx () SERVER fs_heap2;
@@ -519,18 +517,18 @@ FROM pg_class AS pc
     LEFT JOIN pg_am AS pa ON (pa.oid = pc.relam)
 WHERE pc.relname LIKE 'tableam_%_heapx'
 ORDER BY 3, 1, 2;
- relkind | amname |           relname           
----------+--------+-----------------------------
- f       |        | tableam_fdw_heapx
- r       | heap2  | tableam_parted_1_heapx
- r       | heap   | tableam_parted_2_heapx
- p       |        | tableam_parted_heapx
- S       |        | tableam_seq_heapx
- r       | heap2  | tableam_tbl_heapx
- r       | heap2  | tableam_tblas_heapx
- m       | heap2  | tableam_tblmv_heapx
- r       | heap2  | tableam_tblselectinto_heapx
- v       |        | tableam_view_heapx
+ relkind |  amname  |           relname           
+---------+----------+-----------------------------
+ f       |          | tableam_fdw_heapx
+ r       | heap2    | tableam_parted_1_heapx
+ r       | heap     | tableam_parted_2_heapx
+ p       |          | tableam_parted_heapx
+ S       | seqlocal | tableam_seq_heapx
+ r       | heap2    | tableam_tbl_heapx
+ r       | heap2    | tableam_tblas_heapx
+ m       | heap2    | tableam_tblmv_heapx
+ r       | heap2    | tableam_tblselectinto_heapx
+ v       |          | tableam_view_heapx
 (10 rows)
 
 -- don't want to keep those tables, nor the default
@@ -560,3 +558,22 @@ table tableam_parted_b_heap2 depends on access method heap2
 table tableam_parted_d_heap2 depends on access method heap2
 HINT:  Use DROP ... CASCADE to drop the dependent objects too.
 -- we intentionally leave the objects created above alive, to verify pg_dump support
+-- Checks for sequence access methods
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seq_local_sequenceam_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/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 0d734169f1..ea1f1048ea 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1933,6 +1933,18 @@ WHERE p1.oid = a1.amhandler AND a1.amtype = 't' AND
 -----+--------+-----+---------
 (0 rows)
 
+-- check for sequence amhandler functions with the wrong signature
+SELECT a1.oid, a1.amname, p1.oid, p1.proname
+FROM pg_am AS a1, pg_proc AS p1
+WHERE p1.oid = a1.amhandler AND a1.amtype = 's' AND
+    (p1.prorettype != 'sequence_am_handler'::regtype
+     OR p1.proretset
+     OR p1.pronargs != 1
+     OR p1.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname 
+-----+--------+-----+---------
+(0 rows)
+
 -- **************** pg_amop ****************
 -- Look for illegal values in pg_amop fields
 SELECT a1.amopfamily, a1.amopstrategy
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 6aeb7cb963..beb01d78a4 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -5017,31 +5017,33 @@ Indexes:
 -- check printing info about access methods
 \dA
 List of access methods
-  Name  | Type  
---------+-------
- brin   | Index
- btree  | Index
- gin    | Index
- gist   | Index
- hash   | Index
- heap   | Table
- heap2  | Table
- spgist | Index
-(8 rows)
+   Name   |   Type   
+----------+----------
+ brin     | Index
+ btree    | Index
+ gin      | Index
+ gist     | Index
+ hash     | Index
+ heap     | Table
+ heap2    | Table
+ seqlocal | Sequence
+ spgist   | Index
+(9 rows)
 
 \dA *
 List of access methods
-  Name  | Type  
---------+-------
- brin   | Index
- btree  | Index
- gin    | Index
- gist   | Index
- hash   | Index
- heap   | Table
- heap2  | Table
- spgist | Index
-(8 rows)
+   Name   |   Type   
+----------+----------
+ brin     | Index
+ btree    | Index
+ gin      | Index
+ gist     | Index
+ hash     | Index
+ heap     | Table
+ heap2    | Table
+ seqlocal | Sequence
+ spgist   | Index
+(9 rows)
 
 \dA h*
 List of access methods
@@ -5066,32 +5068,34 @@ List of access methods
 
 \dA: extra argument "bar" ignored
 \dA+
-                             List of access methods
-  Name  | Type  |       Handler        |              Description               
---------+-------+----------------------+----------------------------------------
- brin   | Index | brinhandler          | block range index (BRIN) access method
- btree  | Index | bthandler            | b-tree index access method
- gin    | Index | ginhandler           | GIN index access method
- gist   | Index | gisthandler          | GiST index access method
- hash   | Index | hashhandler          | hash index access method
- heap   | Table | heap_tableam_handler | heap table access method
- heap2  | Table | heap_tableam_handler | 
- spgist | Index | spghandler           | SP-GiST index access method
-(8 rows)
+                                   List of access methods
+   Name   |   Type   |           Handler            |              Description               
+----------+----------+------------------------------+----------------------------------------
+ brin     | Index    | brinhandler                  | block range index (BRIN) access method
+ btree    | Index    | bthandler                    | b-tree index access method
+ gin      | Index    | ginhandler                   | GIN index access method
+ gist     | Index    | gisthandler                  | GiST index access method
+ hash     | Index    | hashhandler                  | hash index access method
+ heap     | Table    | heap_tableam_handler         | heap table access method
+ heap2    | Table    | heap_tableam_handler         | 
+ seqlocal | Sequence | seq_local_sequenceam_handler | local sequence access method
+ spgist   | Index    | spghandler                   | SP-GiST index access method
+(9 rows)
 
 \dA+ *
-                             List of access methods
-  Name  | Type  |       Handler        |              Description               
---------+-------+----------------------+----------------------------------------
- brin   | Index | brinhandler          | block range index (BRIN) access method
- btree  | Index | bthandler            | b-tree index access method
- gin    | Index | ginhandler           | GIN index access method
- gist   | Index | gisthandler          | GiST index access method
- hash   | Index | hashhandler          | hash index access method
- heap   | Table | heap_tableam_handler | heap table access method
- heap2  | Table | heap_tableam_handler | 
- spgist | Index | spghandler           | SP-GiST index access method
-(8 rows)
+                                   List of access methods
+   Name   |   Type   |           Handler            |              Description               
+----------+----------+------------------------------+----------------------------------------
+ brin     | Index    | brinhandler                  | block range index (BRIN) access method
+ btree    | Index    | bthandler                    | b-tree index access method
+ gin      | Index    | ginhandler                   | GIN index access method
+ gist     | Index    | gisthandler                  | GiST index access method
+ hash     | Index    | hashhandler                  | hash index access method
+ heap     | Table    | heap_tableam_handler         | heap table access method
+ heap2    | Table    | heap_tableam_handler         | 
+ seqlocal | Sequence | seq_local_sequenceam_handler | local sequence access method
+ spgist   | Index    | spghandler                   | SP-GiST index access method
+(9 rows)
 
 \dA+ h*
                      List of access methods
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index 825aed325e..472289994e 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -117,9 +117,6 @@ SELECT INTO tableam_tblselectinto_heap2 USING heap2 FROM tableam_tbl_heap2;
 -- CREATE VIEW doesn't support USING
 CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
 
--- CREATE SEQUENCE doesn't support USING
-CREATE SEQUENCE tableam_seq_heap2 USING heap2;
-
 -- CREATE MATERIALIZED VIEW does support USING
 CREATE MATERIALIZED VIEW tableam_tblmv_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
 SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1;
@@ -317,9 +314,13 @@ CREATE TABLE tableam_parted_1_heapx PARTITION OF tableam_parted_heapx FOR VALUES
 -- but an explicitly set AM overrides it
 CREATE TABLE tableam_parted_2_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('c', 'd') USING heap;
 
--- sequences, views and foreign servers shouldn't have an AM
-CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx;
+-- sequences have an AM
+SET LOCAL default_sequence_access_method = 'seqlocal';
 CREATE SEQUENCE tableam_seq_heapx;
+RESET default_sequence_access_method;
+
+-- views and foreign servers shouldn't have an AM
+CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx;
 CREATE FOREIGN DATA WRAPPER fdw_heap2 VALIDATOR postgresql_fdw_validator;
 CREATE SERVER fs_heap2 FOREIGN DATA WRAPPER fdw_heap2 ;
 CREATE FOREIGN table tableam_fdw_heapx () SERVER fs_heap2;
@@ -355,3 +356,16 @@ CREATE FOREIGN TABLE fp PARTITION OF tableam_parted_a_heap2 DEFAULT SERVER x;
 DROP ACCESS METHOD heap2;
 
 -- we intentionally leave the objects created above alive, to verify pg_dump support
+
+-- Checks for sequence access methods
+
+-- Create new sequence access method which uses standard local handler
+CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seq_local_sequenceam_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/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 2fe7b6dcc4..1409622374 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1229,6 +1229,16 @@ WHERE p1.oid = a1.amhandler AND a1.amtype = 't' AND
      OR p1.pronargs != 1
      OR p1.proargtypes[0] != 'internal'::regtype);
 
+-- check for sequence amhandler functions with the wrong signature
+
+SELECT a1.oid, a1.amname, p1.oid, p1.proname
+FROM pg_am AS a1, pg_proc AS p1
+WHERE p1.oid = a1.amhandler AND a1.amtype = 's' AND
+    (p1.prorettype != 'sequence_am_handler'::regtype
+     OR p1.proretset
+     OR p1.pronargs != 1
+     OR p1.proargtypes[0] != 'internal'::regtype);
+
 -- **************** pg_amop ****************
 
 -- Look for illegal values in pg_amop fields
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9e951a9e6f..6e1911a76d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2579,6 +2579,7 @@ SeqScanState
 SeqTable
 SeqTableData
 SeqType
+SequenceAmRoutine
 SequenceItem
 SerCommitSeqNo
 SerialControl
@@ -3596,6 +3597,7 @@ lineno_t
 list_sort_comparator
 local_relopt
 local_relopts
+local_sequence_magic
 local_source
 local_ts_iter
 local_ts_radix_tree
@@ -3878,7 +3880,6 @@ scram_state
 scram_state_enum
 security_class_t
 sem_t
-sepgsql_context_info_t
 sequence_magic
 set_join_pathlist_hook_type
 set_rel_pathlist_hook_type
@@ -4100,6 +4101,7 @@ xl_heap_visible
 xl_invalid_page
 xl_invalid_page_key
 xl_invalidations
+xl_local_seq_rec
 xl_logical_message
 xl_multi_insert_tuple
 xl_multixact_create
@@ -4111,7 +4113,6 @@ xl_replorigin_drop
 xl_replorigin_set
 xl_restore_point
 xl_running_xacts
-xl_seq_rec
 xl_smgr_create
 xl_smgr_truncate
 xl_standby_lock
-- 
2.45.2

