Import Petr's seqam v8
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 20 Apr 2015 09:25:36 +0000 (12:25 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 20 Apr 2015 09:25:36 +0000 (12:25 +0300)
40 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/config.sgml
doc/src/sgml/ref/alter_sequence.sgml
doc/src/sgml/ref/create_sequence.sgml
src/backend/access/Makefile
src/backend/access/common/reloptions.c
src/backend/access/sequence/Makefile [new file with mode: 0644]
src/backend/access/sequence/seqam.c [new file with mode: 0644]
src/backend/access/sequence/seqlocal.c [new file with mode: 0644]
src/backend/catalog/Makefile
src/backend/catalog/objectaddress.c
src/backend/commands/indexcmds.c
src/backend/commands/sequence.c
src/backend/commands/tablecmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/backend/utils/cache/catcache.c
src/backend/utils/cache/relcache.c
src/backend/utils/cache/syscache.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/psql/describe.c
src/include/access/reloptions.h
src/include/access/seqam.h [new file with mode: 0644]
src/include/catalog/indexing.h
src/include/catalog/pg_proc.h
src/include/catalog/pg_seqam.h [new file with mode: 0644]
src/include/commands/sequence.h
src/include/nodes/parsenodes.h
src/include/utils/guc.h
src/include/utils/rel.h
src/include/utils/syscache.h
src/test/regress/expected/sanity_check.out
src/test/regress/expected/sequence.out
src/test/regress/expected/updatable_views.out
src/test/regress/sql/sequence.sql

index d0b78f27827cd5b1dd883af248657f33b802d013..361245c6f7b42703f6db54cfb264a96a1d220847 100644 (file)
       <entry>security labels on database objects</entry>
      </row>
 
+     <row>
+      <entry><link linkend="catalog-pg-seqam"><structname>pg_seqam</structname></link></entry>
+      <entry>sequence access methods</entry>
+     </row>
+
      <row>
       <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
       <entry>dependencies on shared objects</entry>
   </table>
  </sect1>
 
+ <sect1 id="catalog-pg-seqam">
+  <title><structname>pg_seqam</structname></title>
+
+  <indexterm zone="catalog-pg-seqam">
+   <primary>pg_am</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_seqam</structname> stores information about
+   sequence access methods. There is one row for each sequence access method
+   installed on the system.
+  </para>
+
+  <table>
+   <title><structname>pg_seqam</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+
+     <row>
+      <entry><structfield>oid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry></entry>
+      <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamreloptions</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to parse and validate <structfield>reloptions</> for the access method</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqaminit</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function called during initialization or <command>RESET</command> of a sequence</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamalloc</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry><quote>Allocate new sequence id</quote> function</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetval</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function implementing <function>setval()</function> interface</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamgetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to dump current state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>seqamsetstate</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to restore a dumped state of a sequence (used mainly by pg_dump)</entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="catalog-pg-shdepend">
   <title><structname>pg_shdepend</structname></title>
 
index b30c68dc138eafacf3b55ac9aa483d8fa4245e0d..ce401067b958e1c132bdaeba2683772e7f680416 100644 (file)
@@ -5671,6 +5671,27 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-serial-sequenceam" xreflabel="serial_sequenceam">
+      <term><varname>serial_sequenceam</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>serial_sequenceam</> configuration parameter</primary>
+      </indexterm>
+      <indexterm><primary>sequence access method</><secondary>serial</></>
+      </term>
+      <listitem>
+       <para>
+        This variable specifies the default sequence access method to be used
+        for <type>SERIAL</> and <type>BIGSERIAL</>.
+       </para>
+
+       <para>
+        The default is 'local' sequence access method. If the value does not
+        match the name of any existing sequence access method, an error will be
+        raised.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-check-function-bodies" xreflabel="check_function_bodies">
       <term><varname>check_function_bodies</varname> (<type>boolean</type>)
       <indexterm>
index 47d3c8291fa5bfc7153255b583f6ee9316a2a5be..8078fc79ee058a490bc768facd3cc0e5351ee16e 100644 (file)
@@ -29,6 +29,7 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
@@ -220,6 +221,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers.
+     </para>
+     <para>
+      When the <literal>RESTART WITH <replaceable
+      class="parameter">restart</replaceable></literal> parameter is also
+      given, it will be used as a starting value for the new access method.
+      Otherwise the <function>nextval</> function will be called with the old
+      access method and the result will be used as start value for the new
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="PARAMETER">new_owner</replaceable></term>
     <listitem>
index 9e364ff24099e5a23f7ee610354403a6aa0a78eb..c7609c6bea9c1ad88015da4eb5b84eb626f16029 100644 (file)
@@ -25,6 +25,7 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
     [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+    [ USING <replaceable class="parameter">access_method</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -223,6 +224,17 @@ SELECT * FROM <replaceable>name</replaceable>;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>USING</literal> option specifies which sequence access
+      method will be used when generating the sequence numbers. The default
+      is <literal>"local"</>.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </refsect1>
 
index 21721b48f0445727d10d33c8322a4e3ca522ae46..818da15defb4c317d10d4ea31d76174f579a9287 100644 (file)
@@ -8,6 +8,7 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS            = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS            = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+                         transam sequence
 
 include $(top_srcdir)/src/backend/common.mk
index 8176b6a6d414b2ac2e0dd69c1db9be4dbcb9c388..9543503e6cd2b23c199680e500dfce52020314c9 100644 (file)
@@ -829,7 +829,8 @@ untransformRelOptions(Datum options)
  * instead.
  *
  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -860,7 +861,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
                        options = view_reloptions(datum, false);
                        break;
                case RELKIND_INDEX:
-                       options = index_reloptions(amoptions, datum, false);
+                       options = am_reloptions(amoptions, datum, false);
+                       break;
+               case RELKIND_SEQUENCE:
+                       options = am_reloptions(amoptions, datum, false);
                        break;
                case RELKIND_FOREIGN_TABLE:
                        options = NULL;
@@ -1308,14 +1312,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 
 
 /*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
  *
  *     amoptions       Oid of option parser
  *     reloptions      options as text[] datum
  *     validate        error flag
  */
 bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
 {
        FmgrInfo        flinfo;
        FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644 (file)
index 0000000..01a0dc8
--- /dev/null
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/sequence
+#
+# IDENTIFICATION
+#    src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644 (file)
index 0000000..6292a1b
--- /dev/null
@@ -0,0 +1,380 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ *       sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default.  Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, am_data.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam.  There is
+ * no syscache for pg_seqam, though the SeqAM data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/seqam.h"
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char   *serial_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+       procedure = &seqrel->rd_aminfo->pname; \
+       if (!OidIsValid(procedure->fn_oid)) \
+       { \
+               RegProcedure    procOid = seqrel->rd_seqam->pname; \
+               if (RegProcedureIsValid(procOid)) \
+                       fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+               else if (!missing_ok) \
+                       elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+       } \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ *  Sequence Access Method API
+ *
+ *  INTERFACE ROUTINES
+ *             seqam_init                      - initialize sequence, also used for resetting
+ *             seqam_alloc                     - allocate a new range of values for the sequence
+ *             seqam_setval            - implements the setval SQL interface
+ *             seqam_get_state         - dump sequence state (for pg_dump)
+ *             seqam_set_state         - restore sequence state (for pg_dump)
+ *
+ * Additionaly, the am_reloptions interface is used for parsing reloptions
+ * that can be used for passing AM specific options.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+extern void
+seqam_init(Oid seqamid, Oid seqrelid, List *seqparams, List *reloptions,
+                  Datum *values, bool *nulls)
+{
+       FmgrInfo        procedure;
+       HeapTuple       tuple = NULL;
+       Form_pg_seqam seqamForm;
+       FunctionCallInfoData fcinfo;
+       char       *validnsps[] = {NULL, NULL};
+       Datum       reloptions_transformed;
+       bytea      *reloptions_parsed;
+
+       tuple = SearchSysCache1(SEQAMOID, seqamid);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "cache lookup failed for sequence access method %u",
+                        seqamid);
+
+       seqamForm = (Form_pg_seqam) GETSTRUCT(tuple);
+
+       fmgr_info(seqamForm->seqaminit, &procedure);
+
+       /* Allow am specific options */
+       validnsps[0] = NameStr(seqamForm->seqamname);
+
+       /*
+        *  Parse AM-specific options, convert to text array form,
+        *  retrieve the AM-option function and then validate.
+        */
+       reloptions_transformed = transformRelOptions((Datum) NULL, reloptions,
+                                                                                                NULL, validnsps, false,
+                                                                                                false);
+
+       reloptions_parsed = am_reloptions(seqamForm->seqamreloptions,
+                                                                         reloptions_transformed, true);
+
+       /*
+        * Have the seqam's proc do it's work.
+        */
+       InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+       fcinfo.arg[0] = ObjectIdGetDatum(seqrelid);
+       fcinfo.arg[1] = PointerGetDatum(seqparams);
+       fcinfo.arg[2] = PointerGetDatum(reloptions_parsed);
+       fcinfo.arg[3] = PointerGetDatum(values);
+       fcinfo.arg[4] = PointerGetDatum(nulls);
+       fcinfo.argnull[0] = false;
+       fcinfo.argnull[1] = false;
+       fcinfo.argnull[2] = false;
+       fcinfo.argnull[3] = false;
+       fcinfo.argnull[4] = false;
+
+       FunctionCallInvoke(&fcinfo);
+
+       ReleaseSysCache(tuple);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+                       int64 *last)
+{
+       FmgrInfo   *procedure;
+       FunctionCallInfoData fcinfo;
+       Datum           ret;
+
+       Assert(RelationIsValid(seqrel));
+       Assert(PointerIsValid(seqrel->rd_seqam));
+       Assert(OidIsValid(seqrel->rd_rel->relam));
+
+       GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+       /*
+        * have the seqam's alloc proc do it's work.
+        */
+       InitFunctionCallInfoData(fcinfo, procedure, 4, InvalidOid, NULL, NULL);
+
+       fcinfo.arg[0] = PointerGetDatum(seqrel);
+       fcinfo.arg[1] = PointerGetDatum(seqh);
+       fcinfo.arg[2] = Int64GetDatum(nrequested);
+       fcinfo.arg[3] = PointerGetDatum(last);
+       fcinfo.argnull[0] = false;
+       fcinfo.argnull[1] = false;
+       fcinfo.argnull[2] = false;
+       fcinfo.argnull[3] = false;
+
+       ret = FunctionCallInvoke(&fcinfo);
+       return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+       FmgrInfo   *procedure;
+       FunctionCallInfoData fcinfo;
+
+       Assert(RelationIsValid(seqrel));
+       Assert(PointerIsValid(seqrel->rd_seqam));
+       Assert(OidIsValid(seqrel->rd_rel->relam));
+
+       GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+       if (!OidIsValid(procedure->fn_oid))
+               return;
+
+       /*
+        * have the seqam's setval proc do it's work.
+        */
+       InitFunctionCallInfoData(fcinfo, procedure, 3, InvalidOid, NULL, NULL);
+
+       fcinfo.arg[0] = PointerGetDatum(seqrel);
+       fcinfo.arg[1] = PointerGetDatum(seqh);
+       fcinfo.arg[2] = Int64GetDatum(new_value);
+       fcinfo.argnull[0] = false;
+       fcinfo.argnull[1] = false;
+       fcinfo.argnull[2] = false;
+
+       FunctionCallInvoke(&fcinfo);
+}
+
+/*
+ * seqam_get_state - pg_dump support
+ */
+int
+seqam_get_state(Relation seqrel, SequenceHandle *seqh, char ***keys,
+                               char ***values)
+{
+       FmgrInfo        procedure;
+       Datum           count;
+       FunctionCallInfoData fcinfo;
+
+       Assert(RelationIsValid(seqrel));
+       Assert(PointerIsValid(seqrel->rd_seqam));
+       Assert(OidIsValid(seqrel->rd_rel->relam));
+
+       fmgr_info(seqrel->rd_seqam->seqamgetstate, &procedure);
+
+       /*
+        * have the seqam's setval proc do it's work.
+        */
+       InitFunctionCallInfoData(fcinfo, &procedure, 4, InvalidOid, NULL, NULL);
+
+       fcinfo.arg[0] = PointerGetDatum(seqrel);
+       fcinfo.arg[1] = PointerGetDatum(seqh);
+       fcinfo.arg[2] = PointerGetDatum(keys);
+       fcinfo.arg[3] = PointerGetDatum(values);
+       fcinfo.argnull[0] = false;
+       fcinfo.argnull[1] = false;
+       fcinfo.argnull[2] = false;
+       fcinfo.argnull[3] = false;
+
+       count = FunctionCallInvoke(&fcinfo);
+
+       return DatumGetInt32(count);
+}
+
+/*
+ * seqam_set_state - restore from pg_dump
+ */
+void
+seqam_set_state(Relation seqrel, SequenceHandle *seqh, char **keys,
+                               char **values, int count)
+{
+       FmgrInfo        procedure;
+       FunctionCallInfoData fcinfo;
+
+       Assert(RelationIsValid(seqrel));
+       Assert(PointerIsValid(seqrel->rd_seqam));
+       Assert(OidIsValid(seqrel->rd_rel->relam));
+
+       fmgr_info(seqrel->rd_seqam->seqamsetstate, &procedure);
+
+       /*
+        * have the seqam's setval proc do it's work.
+        */
+       InitFunctionCallInfoData(fcinfo, &procedure, 5, InvalidOid, NULL, NULL);
+
+       fcinfo.arg[0] = PointerGetDatum(seqrel);
+       fcinfo.arg[1] = PointerGetDatum(seqh);
+       fcinfo.arg[2] = PointerGetDatum(keys);
+       fcinfo.arg[3] = PointerGetDatum(values);
+       fcinfo.arg[4] = Int32GetDatum(count);
+       fcinfo.argnull[0] = false;
+       fcinfo.argnull[1] = false;
+       fcinfo.argnull[2] = false;
+       fcinfo.argnull[3] = false;
+       fcinfo.argnull[4] = false;
+
+       FunctionCallInvoke(&fcinfo);
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new serial_seqam value */
+bool
+check_serial_seqam(char **newval, void **extra, GucSource source)
+{
+       /*
+        * If we aren't inside a transaction, we cannot do database access so
+        * cannot verify the name.  Must accept the value on faith.
+        */
+       if (IsTransactionState())
+       {
+               if (!OidIsValid(get_seqam_oid(*newval, true)))
+               {
+                       /*
+                        * When source == PGC_S_TEST, we are checking the argument of an
+                        * ALTER DATABASE SET or ALTER USER SET command.  Value may
+                        * be created later.  Because of that, issue a NOTICE if source ==
+                        * PGC_S_TEST, but accept the value anyway.
+                        */
+                       if (source == PGC_S_TEST)
+                       {
+                               ereport(NOTICE,
+                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                errmsg("sequence access method \"%s\" does not exist",
+                                                               *newval)));
+                       }
+                       else
+                       {
+                               GUC_check_errdetail("sequence access method \"%s\" does not exist.",
+                                                                       *newval);
+                               return false;
+                       }
+               }
+       }
+       return true;
+}
+
+
+/*
+ * get_seqam_oid - given a sequence AM name, look up the OID
+ *
+ * If missing_ok is false, throw an error if SeqAM name not found.  If true,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+       Oid                     result;
+       HeapTuple       tuple;
+
+       /* look up the access method */
+       tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+       /* We assume that there can be at most one matching tuple */
+       if (HeapTupleIsValid(tuple))
+       {
+               result = HeapTupleGetOid(tuple);
+               ReleaseSysCache(tuple);
+       }
+       else
+               result = InvalidOid;
+
+       if (!OidIsValid(result) && !missing_ok)
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("sequence access method \"%s\" does not exist",
+                                               amname)));
+       return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644 (file)
index 0000000..77edd6b
--- /dev/null
@@ -0,0 +1,340 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ *       Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *        src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS   32
+
+/* Definition of additional columns for local sequence. */
+typedef struct FormLocalSequence
+{
+       int64           log_cnt;
+} FormLocalSequence;
+
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+       Datum   reloptions = PG_GETARG_DATUM(0);
+       bool    validate = PG_GETARG_BOOL(1);
+       bytea  *result;
+
+       result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE);
+       if (result)
+               PG_RETURN_BYTEA_P(result);
+
+       PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+       Oid             seqrelid = PG_GETARG_OID(0);
+       List   *seqoptions = (List *) PG_GETARG_POINTER(1);
+       Datum  *values = (Datum *) PG_GETARG_POINTER(3);
+       bool   *nulls = (bool *) PG_GETARG_POINTER(4);
+       bool    found_restart;
+       int64   start_value,
+                       last_value,
+                       min_value,
+                       max_value;
+       FormLocalSequence *localseq;
+
+       /* Get the new value to use as starting point. */
+       start_value = DatumGetInt64(values[SEQ_COL_STARTVAL - 1]);
+       start_value = sequence_get_restart_value(seqoptions, start_value,
+                                                                                        &found_restart);
+
+       /*
+        * If this is new sequence or restart was provided, use starting value,
+        * otherwise work with our saved value.
+        */
+       if (seqrelid == InvalidOid || found_restart)
+               last_value = start_value;
+       else
+               last_value = DatumGetInt64(values[SEQ_COL_LASTVAL - 1]);
+
+       /* Validate the min/max against the starting point. */
+       min_value = DatumGetInt64(values[SEQ_COL_MINVALUE - 1]);
+       max_value = DatumGetInt64(values[SEQ_COL_MAXVALUE - 1]);
+       sequence_check_range(last_value, min_value, max_value);
+
+       /*
+        * If this is new sequence or RESTART was provided in ALTER we should
+        * reset our state to that new starting point.
+        */
+       if (seqrelid == InvalidOid || found_restart)
+       {
+               nulls[SEQ_COL_LASTVAL - 1] = false;
+               nulls[SEQ_COL_CALLED - 1] = false;
+               values[SEQ_COL_LASTVAL - 1] = Int64GetDatum(last_value);
+               values[SEQ_COL_CALLED - 1] = BoolGetDatum(false);
+       }
+
+       if (nulls[SEQ_COL_AMDATA - 1])
+       {
+               struct varlena *vl = palloc0(VARHDRSZ + sizeof(FormLocalSequence));
+               SET_VARSIZE(vl, VARHDRSZ + sizeof(FormLocalSequence));
+               nulls[SEQ_COL_AMDATA - 1] = false;
+               values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+       }
+
+       localseq = (FormLocalSequence *)
+               VARDATA_ANY(DatumGetByteaP(values[SEQ_COL_AMDATA - 1]));
+
+       /* We always reset the log_cnt. */
+       localseq->log_cnt = 0;
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+       Relation        seqrel = (Relation) PG_GETARG_POINTER(0);
+       SequenceHandle *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+       int64       nrequested = PG_GETARG_INT64(2);
+       int64      *last = (int64 *) PG_GETARG_POINTER(3);
+       FormData_pg_sequence *seq;
+       int64           incby,
+                               maxv,
+                               minv,
+                               log,
+                               fetch,
+                               result,
+                               next,
+                               rescnt = 0;
+       bool            is_cycled,
+                               is_called,
+                               logit = false;
+       FormLocalSequence *localseq;
+
+       seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+       localseq = (FormLocalSequence *) VARDATA_ANY(&seq->amdata);
+
+       next = result = seq->last_value;
+       incby = seq->increment_by;
+       maxv = seq->max_value;
+       minv = seq->min_value;
+       is_cycled = seq->is_cycled;
+       fetch = nrequested;
+       log = localseq->log_cnt;
+       is_called = seq->is_called;
+
+       /* We are returning last_value if not is_called so fetch one less value. */
+       if (!is_called)
+       {
+               nrequested--;
+               fetch--;
+       }
+
+       /*
+        * Decide whether we should emit a WAL log record.  If so, force up the
+        * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+        * cache.  (These will then be usable without logging.)
+        *
+        * If this is the first nextval after a checkpoint, we must force a new
+        * WAL record to be written anyway, else replay starting from the
+        * checkpoint would fail to advance the sequence past the logged values.
+        * In this case we may as well fetch extra values.
+        */
+       if (log < fetch || !is_called)
+       {
+               /* Forced log to satisfy local demand for values. */
+               fetch = log = fetch + SEQ_LOG_VALS;
+               logit = true;
+       }
+       else if (sequence_needs_wal(seqh))
+       {
+               fetch = log = fetch + SEQ_LOG_VALS;
+               logit = true;
+       }
+
+       /* Fetch new result value if is_called. */
+       if (is_called)
+       {
+               rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+                                                                        is_cycled, true);
+               result = next;
+       }
+
+       /* Fetch as many values as was requested by backend. */
+       if (rescnt < nrequested)
+               rescnt += sequence_increment(seqrel, &next, nrequested-rescnt, minv,
+                                                                       maxv, incby, is_cycled, false);
+
+       /* Last value available for calling backend. */
+       *last = next;
+       /* Values we made available to calling backend can't be counted as cached. */
+       log -= rescnt;
+
+       /* We might need to fetch even more values for our own caching. */
+       if (rescnt < fetch)
+               rescnt += sequence_increment(seqrel, &next, fetch-rescnt, minv,
+                                                                       maxv, incby, is_cycled, false);
+
+       fetch -= rescnt;
+       log -= fetch;                           /* adjust for any unfetched numbers */
+       Assert(log >= 0);
+
+       /*
+        * Log our cached data.
+        *
+        * XXX: Does this need a critical section that would encapsulate both
+        * changes?
+        */
+       if (logit)
+       {
+               seq->last_value = next;
+               seq->is_called = true;
+               localseq->log_cnt = 0;
+
+               sequence_save_tuple(seqh, NULL, true);
+       }
+
+       /* Now update sequence tuple to the intended final state */
+       seq->last_value = *last;                /* last fetched number */
+       seq->is_called = true;
+       localseq->log_cnt = log;                        /* how much is logged */
+
+       sequence_save_tuple(seqh, NULL, false);
+
+       PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+       SequenceHandle     *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+       int64                           next = PG_GETARG_INT64(2);
+       FormData_pg_sequence  *seq;
+       FormLocalSequence *localseq;
+
+       seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+       localseq = (FormLocalSequence *) VARDATA_ANY(&seq->amdata);
+
+       seq->last_value = next;         /* last fetched number */
+       seq->is_called = true;
+       localseq->log_cnt = 0;          /* how much is logged */
+
+       sequence_save_tuple(seqh, NULL, true);
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+Datum
+seqam_local_get_state(PG_FUNCTION_ARGS)
+{
+       SequenceHandle     *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+       char                     ***out_keys = (char ***) PG_GETARG_POINTER(2);
+       char                     ***out_values = (char ***) PG_GETARG_POINTER(3);
+       char                      **keys;
+       char                      **values;
+       FormData_pg_sequence  *seq;
+
+       seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+       keys = palloc(2 * sizeof(char *));
+       values = palloc(2 * sizeof(char *));
+
+       keys[0] = "last_value";
+       values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+                                                                               Int64GetDatum(seq->last_value)));
+
+       keys[1] = "is_called";
+       values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+                                                                               BoolGetDatum(seq->is_called)));
+
+       *out_keys = keys;
+       *out_values = values;
+
+       PG_RETURN_INT32(2);
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumpred state of local sequence (used by pg_dump)
+*/
+Datum
+seqam_local_set_state(PG_FUNCTION_ARGS)
+{
+       SequenceHandle     *seqh = (SequenceHandle*) PG_GETARG_POINTER(1);
+       char                      **keys = (char **) PG_GETARG_POINTER(2);
+       char                      **values = (char **) PG_GETARG_POINTER(3);
+       int                                     count = PG_GETARG_INT32(4);
+       FormData_pg_sequence  *seq;
+       int                                     i;
+
+       seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+       for (i = 0; i < count; i++)
+       {
+               if (pg_strcasecmp(keys[i], "last_value") == 0)
+                       seq->last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+                                                                                               CStringGetDatum(values[i])));
+               else if (pg_strcasecmp(keys[i], "is_called") == 0)
+                       seq->is_called = DatumGetBool(DirectFunctionCall1(boolin,
+                                                                                               CStringGetDatum(values[i])));
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("invalid state key \"%s\" for local sequence",
+                                                       keys[i])));
+       }
+
+       sequence_save_tuple(seqh, NULL, true);
+
+       PG_RETURN_VOID();
+}
index a403c643600b82201ff6e8f0f7e310db6054839a..147c571dc14c951c68b8d96275ed93e312cf9b22 100644 (file)
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
        pg_statistic.h pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \
        pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
        pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
-       pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+       pg_authid.h pg_auth_members.h pg_seqam.h pg_shdepend.h pg_shdescription.h \
        pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
        pg_ts_parser.h pg_ts_template.h pg_extension.h \
        pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
index 30cb6990726945d4aa81e6424a0c51f7d5db5526..a26d67d0c167602f051ee50774f0b3a667f57af7 100644 (file)
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/seqam.h"
 #include "access/sysattr.h"
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
index 99acd4a6a2ca614c19481c98e588b57bad7925ff..0c1cf58b0bcb202225a832ab8e9b9c0081ee0f84 100644 (file)
@@ -527,7 +527,7 @@ DefineIndex(Oid relationId,
        reloptions = transformRelOptions((Datum) 0, stmt->options,
                                                                         NULL, NULL, false, false);
 
-       (void) index_reloptions(amoptions, reloptions, true);
+       (void) am_reloptions(amoptions, reloptions, true);
 
        /*
         * Prepare arguments for index_create, primarily an IndexInfo structure.
index 6d316d62b6c58ca0c49ad3b6b925e9db23f5432b..1089e40d1d1a12d715939d44a5ed3816e85f88d6 100644 (file)
@@ -14,6 +14,9 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
-/*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS   32
-
 /*
  * The "special area" of a sequence's buffer page looks like this.
  */
@@ -81,6 +80,14 @@ typedef SeqTableData *SeqTable;
 
 static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 
+struct SequenceHandle
+{
+       SeqTable        elm;
+       Relation        rel;
+       Buffer          buf;
+       HeapTupleData   tup;
+};
+
 /*
  * last_used_seq is updated by nextval() to point to the last used
  * sequence.
@@ -91,13 +98,13 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
 static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
-                          Buffer *buf, HeapTuple seqtuple);
 static void init_params(List *options, bool isInit,
                        Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+                                                          Buffer buf, Page page);
+static void seqrel_update_relam(Oid seqoid, Oid seqamid);
+static Oid get_new_seqam_oid(Oid oldAM, char *accessMethod);
 
 
 /*
@@ -112,6 +119,7 @@ DefineSequence(CreateSeqStmt *seq)
        CreateStmt *stmt = makeNode(CreateStmt);
        Oid                     seqoid;
        ObjectAddress address;
+       Oid                     seqamid;
        Relation        rel;
        HeapTuple       tuple;
        TupleDesc       tupDesc;
@@ -207,11 +215,6 @@ DefineSequence(CreateSeqStmt *seq)
                                coldef->colname = "cache_value";
                                value[i - 1] = Int64GetDatumFast(new.cache_value);
                                break;
-                       case SEQ_COL_LOG:
-                               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
-                               coldef->colname = "log_cnt";
-                               value[i - 1] = Int64GetDatum((int64) 0);
-                               break;
                        case SEQ_COL_CYCLE:
                                coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
                                coldef->colname = "is_cycled";
@@ -222,6 +225,12 @@ DefineSequence(CreateSeqStmt *seq)
                                coldef->colname = "is_called";
                                value[i - 1] = BoolGetDatum(false);
                                break;
+                       case SEQ_COL_AMDATA:
+                               coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+                               coldef->colname = "amdata";
+                               null[i - 1] = true;
+                               value[i - 1] = (Datum) 0;
+                               break;
                }
                stmt->tableElts = lappend(stmt->tableElts, coldef);
        }
@@ -234,10 +243,23 @@ DefineSequence(CreateSeqStmt *seq)
        stmt->tablespacename = NULL;
        stmt->if_not_exists = seq->if_not_exists;
 
+       /* Let AM fill the values[] and nulls[] for the tuple as well. */
+       seqamid = get_new_seqam_oid(InvalidOid, seq->accessMethod);
+       seqam_init(seqamid, InvalidOid, seq->options, seq->amoptions,
+                          value, null);
+
        address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
        seqoid = address.objectId;
        Assert(seqoid != InvalidOid);
 
+       /*
+        * After we've created the sequence's relation in pg_class, update
+        * the relam to a non-default value, if requested. We perform this
+        * as a separate update to avoid invasive changes in normal code
+        * paths and to keep the code similar between CREATE and ALTER.
+        */
+       seqrel_update_relam(seqoid, seqamid);
+
        rel = heap_open(seqoid, AccessExclusiveLock);
        tupDesc = RelationGetDescr(rel);
 
@@ -254,6 +276,7 @@ DefineSequence(CreateSeqStmt *seq)
        return address;
 }
 
+
 /*
  * Reset a sequence to its initial value.
  *
@@ -267,58 +290,70 @@ DefineSequence(CreateSeqStmt *seq)
  * responsible for permissions checking.
  */
 void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
 {
-       Relation        seq_rel;
-       SeqTable        elm;
-       Form_pg_sequence seq;
-       Buffer          buf;
-       HeapTupleData seqtuple;
        HeapTuple       tuple;
+       HeapTuple       newtup;
+       Relation        seqrel;
+       TupleDesc       tupDesc;
+       Datum      *values;
+       bool       *nulls;
+       bool       *replaces;
+       SequenceHandle seqh;
 
        /*
-        * Read the old sequence.  This does a bit more work than really
-        * necessary, but it's simple, and we do want to double-check that it's
-        * indeed a sequence.
+        * Read and lock the old page.
         */
-       init_sequence(seq_relid, &elm, &seq_rel);
-       (void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+       sequence_open(seqrelid, &seqh);
+       tuple = sequence_read_tuple(&seqh);
 
        /*
         * Copy the existing sequence tuple.
         */
-       tuple = heap_copytuple(&seqtuple);
+       tuple = heap_copytuple(tuple);
 
        /* Now we're done with the old page */
-       UnlockReleaseBuffer(buf);
+       sequence_release_tuple(&seqh);
 
        /*
-        * Modify the copied tuple to execute the restart (compare the RESTART
-        * action in AlterSequence)
+        * Tell AM to reset the sequence.
+        * This fakes the ALTER SEQUENCE RESTART command from the
+        * Sequence AM perspective.
         */
-       seq = (Form_pg_sequence) GETSTRUCT(tuple);
-       seq->last_value = seq->start_value;
-       seq->is_called = false;
-       seq->log_cnt = 0;
+       seqrel = seqh.rel;
+       tupDesc = RelationGetDescr(seqrel);
+       values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+       nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+       replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+       memset(replaces, true, tupDesc->natts * sizeof(bool));
+
+       heap_deform_tuple(tuple, tupDesc, values, nulls);
+
+       seqam_init(seqrel->rd_rel->relam, seqrelid,
+                          list_make1(makeDefElem("restart", NULL)), NULL,
+                          values, nulls);
+
+       newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
 
        /*
         * Create a new storage file for the sequence.  We want to keep the
         * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
         * Same with relminmxid, since a sequence will never contain multixacts.
         */
-       RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+       RelationSetNewRelfilenode(seqh.rel, seqh.rel->rd_rel->relpersistence,
                                                          InvalidTransactionId, InvalidMultiXactId);
 
        /*
         * Insert the modified tuple into the new storage file.
         */
-       fill_seq_with_data(seq_rel, tuple);
+       fill_seq_with_data(seqh.rel, newtup);
 
        /* Clear local cache so that we don't think we have cached numbers */
        /* Note that we do not change the currval() state */
-       elm->cached = elm->last;
+       seqh.elm->cached = seqh.elm->last;
 
-       relation_close(seq_rel, NoLock);
+       /* And we're done, close the sequence. */
+       sequence_close(&seqh);
 }
 
 /*
@@ -361,7 +396,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
        tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
        ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
 
-       /* check the comment above nextval_internal()'s equivalent call. */
+       /* check the comment above sequence_save_tuple()'s equivalent call. */
        if (RelationNeedsWAL(rel))
                GetTopTransactionId();
 
@@ -375,23 +410,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
                elog(ERROR, "failed to add sequence tuple to page");
 
        /* XLOG stuff */
-       if (RelationNeedsWAL(rel))
-       {
-               xl_seq_rec      xlrec;
-               XLogRecPtr      recptr;
-
-               XLogBeginInsert();
-               XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-               xlrec.node = rel->rd_node;
-
-               XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-               XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
-               recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-               PageSetLSN(page, recptr);
-       }
+       log_sequence_tuple(rel, tuple, buf, page);
 
        END_CRIT_SECTION();
 
@@ -406,19 +425,27 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 ObjectAddress
 AlterSequence(AlterSeqStmt *stmt)
 {
-       Oid                     relid;
+       Oid                     seqrelid;
+       Oid                     oldamid;
+       Oid                     seqamid;
        SeqTable        elm;
+       HeapTuple       tuple;
+       HeapTuple       newtup;
        Relation        seqrel;
-       Buffer          buf;
-       HeapTupleData seqtuple;
-       Form_pg_sequence seq;
-       FormData_pg_sequence new;
+       Form_pg_sequence new;
        List       *owned_by;
        ObjectAddress address;
+       TupleDesc       tupDesc;
+       Datum      *values;
+       bool       *nulls;
+       bool       *replaces;
+       List       *seqoptions;
+       SequenceHandle seqh;
 
        /* Open and lock sequence. */
-       relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
-       if (relid == InvalidOid)
+       seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+       if (seqrelid == InvalidOid)
        {
                ereport(NOTICE,
                                (errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +453,111 @@ AlterSequence(AlterSeqStmt *stmt)
                return InvalidObjectAddress;
        }
 
-       init_sequence(relid, &elm, &seqrel);
+       sequence_open(seqrelid, &seqh);
+       elm = seqh.elm;
+       seqrel = seqh.rel;
 
        /* allow ALTER to sequence owner only */
-       if (!pg_class_ownercheck(relid, GetUserId()))
+       if (!pg_class_ownercheck(seqrelid, GetUserId()))
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
                                           stmt->sequence->relname);
 
        /* lock page' buffer and read tuple into new sequence structure */
-       seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+       tuple = sequence_read_tuple(&seqh);
 
        /* Copy old values of options into workspace */
-       memcpy(&new, seq, sizeof(FormData_pg_sequence));
+       tuple = heap_copytuple(tuple);
+       new = (Form_pg_sequence) GETSTRUCT(tuple);
 
        /* Check and set new values */
-       init_params(stmt->options, false, &new, &owned_by);
+       seqoptions = stmt->options;
+       init_params(seqoptions, false, new, &owned_by);
 
-       /* Clear local cache so that we don't think we have cached numbers */
-       /* Note that we do not change the currval() state */
-       elm->cached = elm->last;
+       tupDesc = RelationGetDescr(seqrel);
+       values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+       nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
 
-       /* check the comment above nextval_internal()'s equivalent call. */
-       if (RelationNeedsWAL(seqrel))
-               GetTopTransactionId();
+       heap_deform_tuple(tuple, tupDesc, values, nulls);
 
-       /* Now okay to update the on-disk tuple */
-       START_CRIT_SECTION();
+       oldamid = seqrel->rd_rel->relam;
+       seqamid = get_new_seqam_oid(seqrel->rd_rel->relam, stmt->accessMethod);
+
+       /*
+        * If we are changing sequence AM, we need to alter
+        * the sequence relation.
+        */
+       if (seqamid != oldamid)
+       {
+               bool    found;
 
-       memcpy(seq, &new, sizeof(FormData_pg_sequence));
+               /*
+                * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+                * statement, we use nextval of the old sequence AM to provide
+                * restart point for the new sequence AM.
+                *
+                * This may not be the most beautiful way to do it but since
+                * sequence AMs are expected to handle the RESTART option anyway,
+                * it does not seem neccessary to invent special parameter for the
+                * init API just for this.
+                */
+               (void) sequence_get_restart_value(seqoptions, 0, &found);
+               if (!found)
+               {
+                       DefElem    *defel;
+                       int64           last,
+                                               restart_value;
 
-       MarkBufferDirty(buf);
+                       restart_value = seqam_alloc(seqrel, &seqh, 1, &last);
 
-       /* XLOG stuff */
-       if (RelationNeedsWAL(seqrel))
-       {
-               xl_seq_rec      xlrec;
-               XLogRecPtr      recptr;
-               Page            page = BufferGetPage(buf);
+                       defel = makeDefElem("restart", (Node *)makeInteger(restart_value));
+                       seqoptions = lcons(defel, seqoptions);
+               }
 
-               XLogBeginInsert();
-               XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+               /* We don't need the old sequence tuple anymore. */
+               sequence_release_tuple(&seqh);
 
-               xlrec.node = seqrel->rd_node;
-               XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+               /*
+                * Create a new storage file for the sequence.
+                * See ResetSequence for more info.
+                */
+               RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+                                                                 InvalidTransactionId, InvalidMultiXactId);
 
-               XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+               /* Let the new sequence AM initialize. */
+               seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+                                  values, nulls);
 
-               recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+               /* Rebuild the sequence tuple and save it. */
+               newtup = heap_form_tuple(tupDesc, values, nulls);
+               fill_seq_with_data(seqh.rel, newtup);
 
-               PageSetLSN(page, recptr);
+               /* Update the catalog. */
+               seqrel_update_relam(seqrelid, seqamid);
+       }
+       else
+       {
+               /* Let sequence AM update the tuple. */
+               replaces = (bool *) palloc(tupDesc->natts * sizeof(bool));
+               memset(replaces, true, tupDesc->natts * sizeof(bool));
+               seqam_init(seqamid, seqrelid, seqoptions, stmt->amoptions,
+                                  values, nulls);
+               newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replaces);
+               sequence_save_tuple(&seqh, newtup, true);
        }
 
-       END_CRIT_SECTION();
-
-       UnlockReleaseBuffer(buf);
+       /* Clear local cache so that we don't think we have cached numbers */
+       /* Note that we do not change the currval() state */
+       elm->cached = elm->last;
 
        /* process OWNED BY if given */
        if (owned_by)
                process_owned_by(seqrel, owned_by);
 
-       InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+       InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
 
-       ObjectAddressSet(address, RelationRelationId, relid);
+       ObjectAddressSet(address, RelationRelationId, seqrelid);
 
-       relation_close(seqrel, NoLock);
+       sequence_close(&seqh);
 
        return address;
 }
@@ -530,29 +598,24 @@ nextval_oid(PG_FUNCTION_ARGS)
        PG_RETURN_INT64(nextval_internal(relid));
 }
 
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
 static int64
 nextval_internal(Oid relid)
 {
        SeqTable        elm;
        Relation        seqrel;
-       Buffer          buf;
-       Page            page;
-       HeapTupleData seqtuple;
-       Form_pg_sequence seq;
-       int64           incby,
-                               maxv,
-                               minv,
-                               cache,
-                               log,
-                               fetch,
-                               last;
-       int64           result,
-                               next,
-                               rescnt = 0;
-       bool            logit = false;
+       Form_pg_sequence seq_form;
+       int64           last,
+                               result;
+       SequenceHandle seqh;
 
        /* open and AccessShareLock sequence */
-       init_sequence(relid, &elm, &seqrel);
+       sequence_open(relid, &seqh);
+       elm = seqh.elm;
+       seqrel = seqh.rel;
 
        if (pg_class_aclcheck(elm->relid, GetUserId(),
                                                  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -570,121 +633,15 @@ nextval_internal(Oid relid)
                Assert(elm->last_valid);
                Assert(elm->increment != 0);
                elm->last += elm->increment;
-               relation_close(seqrel, NoLock);
+               sequence_close(&seqh);
                last_used_seq = elm;
                return elm->last;
        }
 
        /* lock page' buffer and read tuple */
-       seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-       page = BufferGetPage(buf);
-
-       last = next = result = seq->last_value;
-       incby = seq->increment_by;
-       maxv = seq->max_value;
-       minv = seq->min_value;
-       fetch = cache = seq->cache_value;
-       log = seq->log_cnt;
-
-       if (!seq->is_called)
-       {
-               rescnt++;                               /* return last_value if not is_called */
-               fetch--;
-       }
-
-       /*
-        * Decide whether we should emit a WAL log record.  If so, force up the
-        * fetch count to grab SEQ_LOG_VALS more values than we actually need to
-        * cache.  (These will then be usable without logging.)
-        *
-        * If this is the first nextval after a checkpoint, we must force a new
-        * WAL record to be written anyway, else replay starting from the
-        * checkpoint would fail to advance the sequence past the logged values.
-        * In this case we may as well fetch extra values.
-        */
-       if (log < fetch || !seq->is_called)
-       {
-               /* forced log to satisfy local demand for values */
-               fetch = log = fetch + SEQ_LOG_VALS;
-               logit = true;
-       }
-       else
-       {
-               XLogRecPtr      redoptr = GetRedoRecPtr();
-
-               if (PageGetLSN(page) <= redoptr)
-               {
-                       /* last update of seq was before checkpoint */
-                       fetch = log = fetch + SEQ_LOG_VALS;
-                       logit = true;
-               }
-       }
-
-       while (fetch)                           /* try to fetch cache [+ log ] numbers */
-       {
-               /*
-                * Check MAXVALUE for ascending sequences and MINVALUE for descending
-                * sequences
-                */
-               if (incby > 0)
-               {
-                       /* ascending sequence */
-                       if ((maxv >= 0 && next > maxv - incby) ||
-                               (maxv < 0 && next + incby > maxv))
-                       {
-                               if (rescnt > 0)
-                                       break;          /* stop fetching */
-                               if (!seq->is_cycled)
-                               {
-                                       char            buf[100];
-
-                                       snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
-                                       ereport(ERROR,
-                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-                                                  errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
-                                                                 RelationGetRelationName(seqrel), buf)));
-                               }
-                               next = minv;
-                       }
-                       else
-                               next += incby;
-               }
-               else
-               {
-                       /* descending sequence */
-                       if ((minv < 0 && next < minv - incby) ||
-                               (minv >= 0 && next + incby < minv))
-                       {
-                               if (rescnt > 0)
-                                       break;          /* stop fetching */
-                               if (!seq->is_cycled)
-                               {
-                                       char            buf[100];
-
-                                       snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
-                                       ereport(ERROR,
-                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-                                                  errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
-                                                                 RelationGetRelationName(seqrel), buf)));
-                               }
-                               next = maxv;
-                       }
-                       else
-                               next += incby;
-               }
-               fetch--;
-               if (rescnt < cache)
-               {
-                       log--;
-                       rescnt++;
-                       last = next;
-                       if (rescnt == 1)        /* if it's first result - */
-                               result = next;  /* it's what to return */
-               }
-       }
+       seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
-       log -= fetch;                           /* adjust for any unfetched numbers */
-       Assert(log >= 0);
+       result = seqam_alloc(seqrel, &seqh, seq_form->cache_value, &last);
 
        /* save info in local cache */
        elm->last = result;                     /* last returned number */
@@ -693,101 +650,38 @@ nextval_internal(Oid relid)
 
        last_used_seq = elm;
 
-       /*
-        * If something needs to be WAL logged, acquire an xid, so this
-        * transaction's commit will trigger a WAL flush and wait for
-        * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
-        * no need to assign xids subxacts, that'll already trigger a appropriate
-        * wait.  (Have to do that here, so we're outside the critical section)
-        */
-       if (logit && RelationNeedsWAL(seqrel))
-               GetTopTransactionId();
-
-       /* ready to change the on-disk (or really, in-buffer) tuple */
-       START_CRIT_SECTION();
-
-       /*
-        * We must mark the buffer dirty before doing XLogInsert(); see notes in
-        * SyncOneBuffer().  However, we don't apply the desired changes just yet.
-        * This looks like a violation of the buffer update protocol, but it is in
-        * fact safe because we hold exclusive lock on the buffer.  Any other
-        * process, including a checkpoint, that tries to examine the buffer
-        * contents will block until we release the lock, and then will see the
-        * final state that we install below.
-        */
-       MarkBufferDirty(buf);
-
-       /* XLOG stuff */
-       if (logit && RelationNeedsWAL(seqrel))
-       {
-               xl_seq_rec      xlrec;
-               XLogRecPtr      recptr;
-
-               /*
-                * We don't log the current state of the tuple, but rather the state
-                * as it would appear after "log" more fetches.  This lets us skip
-                * that many future WAL records, at the cost that we lose those
-                * sequence values if we crash.
-                */
-               XLogBeginInsert();
-               XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-               /* set values that will be saved in xlog */
-               seq->last_value = next;
-               seq->is_called = true;
-               seq->log_cnt = 0;
-
-               xlrec.node = seqrel->rd_node;
-
-               XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-               XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-               recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-               PageSetLSN(page, recptr);
-       }
-
-       /* Now update sequence tuple to the intended final state */
-       seq->last_value = last;         /* last fetched number */
-       seq->is_called = true;
-       seq->log_cnt = log;                     /* how much is logged */
-
-       END_CRIT_SECTION();
-
-       UnlockReleaseBuffer(buf);
-
-       relation_close(seqrel, NoLock);
+       sequence_close(&seqh);
 
        return result;
 }
 
+
 Datum
 currval_oid(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       SeqTable        elm;
-       Relation        seqrel;
+       SequenceHandle seqh;
 
        /* open and AccessShareLock sequence */
-       init_sequence(relid, &elm, &seqrel);
+       sequence_open(relid, &seqh);
 
-       if (pg_class_aclcheck(elm->relid, GetUserId(),
+       if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
                                                  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("permission denied for sequence %s",
-                                               RelationGetRelationName(seqrel))));
+                                               RelationGetRelationName(seqh.rel))));
 
-       if (!elm->last_valid)
+       if (!seqh.elm->last_valid)
                ereport(ERROR,
                                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                                 errmsg("currval of sequence \"%s\" is not yet defined in this session",
-                                               RelationGetRelationName(seqrel))));
+                                               RelationGetRelationName(seqh.rel))));
 
-       result = elm->last;
+       result = seqh.elm->last;
 
-       relation_close(seqrel, NoLock);
+       sequence_close(&seqh);
 
        PG_RETURN_INT64(result);
 }
@@ -828,31 +722,24 @@ lastval(PG_FUNCTION_ARGS)
 }
 
 /*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
  */
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
 {
+       Oid                     relid = PG_GETARG_OID(0);
+       int64           next = PG_GETARG_INT64(1);
        SeqTable        elm;
        Relation        seqrel;
-       Buffer          buf;
-       HeapTupleData seqtuple;
-       Form_pg_sequence seq;
+       SequenceHandle seqh;
 
        /* open and AccessShareLock sequence */
-       init_sequence(relid, &elm, &seqrel);
+       sequence_open(relid, &seqh);
+       elm = seqh.elm;
+       seqrel = seqh.rel;
 
-       if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+       if (pg_class_aclcheck(elm->relid, GetUserId(),
+                                                 ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("permission denied for sequence %s",
@@ -862,92 +749,27 @@ do_setval(Oid relid, int64 next, bool iscalled)
        if (!seqrel->rd_islocaltemp)
                PreventCommandIfReadOnly("setval()");
 
-       /* lock page' buffer and read tuple */
-       seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
-
-       if ((next < seq->min_value) || (next > seq->max_value))
-       {
-               char            bufv[100],
-                                       bufm[100],
-                                       bufx[100];
-
-               snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
-               snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
-               snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
-               ereport(ERROR,
-                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-                                errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
-                                               bufv, RelationGetRelationName(seqrel),
-                                               bufm, bufx)));
-       }
-
-       /* Set the currval() state only if iscalled = true */
-       if (iscalled)
-       {
-               elm->last = next;               /* last returned number */
-               elm->last_valid = true;
-       }
+       seqam_setval(seqrel, &seqh, next);
 
-       /* In any case, forget any future cached numbers */
+       /* Reset local cached data */
+       elm->last = next;               /* last returned number */
+       elm->last_valid = true;
        elm->cached = elm->last;
 
-       /* check the comment above nextval_internal()'s equivalent call. */
-       if (RelationNeedsWAL(seqrel))
-               GetTopTransactionId();
-
-       /* ready to change the on-disk (or really, in-buffer) tuple */
-       START_CRIT_SECTION();
-
-       seq->last_value = next;         /* last fetched number */
-       seq->is_called = iscalled;
-       seq->log_cnt = 0;
-
-       MarkBufferDirty(buf);
-
-       /* XLOG stuff */
-       if (RelationNeedsWAL(seqrel))
-       {
-               xl_seq_rec      xlrec;
-               XLogRecPtr      recptr;
-               Page            page = BufferGetPage(buf);
-
-               XLogBeginInsert();
-               XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
-               xlrec.node = seqrel->rd_node;
-               XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-               XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
-               recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
-               PageSetLSN(page, recptr);
-       }
-
-       END_CRIT_SECTION();
-
-       UnlockReleaseBuffer(buf);
-
-       relation_close(seqrel, NoLock);
-}
-
-/*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval_oid(PG_FUNCTION_ARGS)
-{
-       Oid                     relid = PG_GETARG_OID(0);
-       int64           next = PG_GETARG_INT64(1);
+       last_used_seq = elm;
 
-       do_setval(relid, next, true);
+       sequence_close(&seqh);
 
        PG_RETURN_INT64(next);
 }
 
 /*
  * Implement the 3 arg setval procedure.
- * See do_setval for discussion.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
  */
 Datum
 setval3_oid(PG_FUNCTION_ARGS)
@@ -955,15 +777,60 @@ setval3_oid(PG_FUNCTION_ARGS)
        Oid                     relid = PG_GETARG_OID(0);
        int64           next = PG_GETARG_INT64(1);
        bool            iscalled = PG_GETARG_BOOL(2);
+       char       *keys[2] = {"last_value", "is_called"};
+       char       *values[2];
+       SeqTable        elm;
+       Relation        seqrel;
+       SequenceHandle seqh;
 
-       do_setval(relid, next, iscalled);
+       /* open and AccessShareLock sequence */
+       sequence_open(relid, &seqh);
+       elm = seqh.elm;
+       seqrel = seqh.rel;
 
-       PG_RETURN_INT64(next);
-}
+       if (pg_class_aclcheck(elm->relid, GetUserId(),
+                                                 ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("permission denied for sequence %s",
+                                                       RelationGetRelationName(seqrel))));
 
+       /* read-only transactions may only modify temp sequences */
+       if (!seqrel->rd_islocaltemp)
+               PreventCommandIfReadOnly("setval()");
 
-/*
- * Open the sequence and acquire AccessShareLock if needed
+       /* Make sure the target sequence is 'local' sequence. */
+       if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+       /* Convert the data into 'local' sequence dump format and call restore API. */
+       values[0] = DatumGetCString(DirectFunctionCall1(int8out,
+                                                                                                       Int64GetDatum(next)));
+       values[1] = DatumGetCString(DirectFunctionCall1(boolout,
+                                                                                                       BoolGetDatum(iscalled)));
+       seqam_set_state(seqh.rel, &seqh, keys, values, 2);
+
+       /* Set the currval() state only if iscalled = true */
+       if (iscalled)
+       {
+               elm->last = next;               /* last returned number */
+               elm->last_valid = true;
+       }
+
+       /* Reset local cached data */
+       elm->cached = elm->last;
+
+       last_used_seq = elm;
+
+       sequence_close(&seqh);
+
+       PG_RETURN_INT64(next);
+}
+
+/*
+ * Open the sequence and acquire AccessShareLock if needed
  *
  * If we haven't touched the sequence already in this transaction,
  * we need to acquire AccessShareLock.  We arrange for the lock to
@@ -1020,21 +887,20 @@ create_seq_hashtable(void)
 }
 
 /*
- * Given a relation OID, open and lock the sequence.  p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
  */
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid seqrelid, SequenceHandle *seqh)
 {
-       SeqTable        elm;
-       Relation        seqrel;
-       bool            found;
+       SeqTable                elm;
+       Relation                seqrel;
+       bool                    found;
 
        /* Find or create a hash table entry for this sequence */
        if (seqhashtab == NULL)
                create_seq_hashtable();
 
-       elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);
+       elm = (SeqTable) hash_search(seqhashtab, &seqrelid, HASH_ENTER, &found);
 
        /*
         * Initialize the new hash table entry if it did not exist already.
@@ -1076,44 +942,57 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
        }
 
        /* Return results */
-       *p_elm = elm;
-       *p_rel = seqrel;
+       seqh->elm = elm;
+       seqh->rel = seqrel;
+       seqh->buf = InvalidBuffer;
+       seqh->tup.t_data = NULL;
+       seqh->tup.t_len = 0;
 }
 
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+       sequence_release_tuple(seqh);
+
+       relation_close(seqh->rel, NoLock);
+}
 
 /*
  * Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- *             (this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
  */
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
 {
        Page            page;
+       Buffer          buf;
        ItemId          lp;
        sequence_magic *sm;
-       Form_pg_sequence seq;
+       Form_pg_sequence seq_form;
 
-       *buf = ReadBuffer(rel, 0);
-       LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+       if (seqh->tup.t_data != NULL)
+       {
+               return &seqh->tup;
+       }
+
+       seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+       LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
-       page = BufferGetPage(*buf);
+       page = BufferGetPage(buf);
        sm = (sequence_magic *) PageGetSpecialPointer(page);
 
        if (sm->magic != SEQ_MAGIC)
                elog(ERROR, "bad magic number in sequence \"%s\": %08X",
-                        RelationGetRelationName(rel), sm->magic);
+                        RelationGetRelationName(seqh->rel), sm->magic);
 
        lp = PageGetItemId(page, FirstOffsetNumber);
        Assert(ItemIdIsNormal(lp));
 
-       /* Note we currently only bother to set these two fields of *seqtuple */
-       seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-       seqtuple->t_len = ItemIdGetLength(lp);
+       /* Note we currently only bother to set these two fields of seqh->tup */
+       seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+       seqh->tup.t_len = ItemIdGetLength(lp);
 
        /*
         * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1123,33 +1002,149 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
         * bit update, ie, don't bother to WAL-log it, since we can certainly do
         * this again if the update gets lost.
         */
-       Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
-       if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+       Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+       if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
        {
-               HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
-               seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
-               seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
-               MarkBufferDirtyHint(*buf, true);
+               HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+               seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+               seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+               MarkBufferDirtyHint(buf, true);
        }
 
-       seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+       seq_form = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
 
        /* this is a handy place to update our copy of the increment */
-       elm->increment = seq->increment_by;
+       seqh->elm->increment = seq_form->increment_by;
 
-       return seq;
+       return &seqh->tup;
 }
 
 /*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * Update the page, optionally do WAL logging of the tuple
+ */
+void
+sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup, bool do_wal)
+{
+       Page    page;
+
+       Assert(seqh->tup.t_data != NULL);
+
+       page = BufferGetPage(seqh->buf);
+
+       /*
+        * If something needs to be WAL logged, acquire an xid, so this
+        * transaction's commit will trigger a WAL flush and wait for
+        * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+        * no need to assign xids subxacts, that'll already trigger a appropriate
+        * wait.  (Have to do that here, so we're outside the critical section)
+        */
+       if (do_wal && RelationNeedsWAL(seqh->rel))
+               GetTopTransactionId();
+
+       if (HeapTupleIsValid(newtup))
+       {
+               /*
+                * New tuple was passed, we must process it and replace the old one on
+                * the same page.
+                */
+               Page    temppage;
+
+               /* Sequence tuples are always frozen. */
+               HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+               HeapTupleHeaderSetXminFrozen(newtup->t_data);
+               HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+               HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+               newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+               ItemPointerSet(&newtup->t_data->t_ctid, 0, FirstOffsetNumber);
+
+               /*
+                * Replace the original tuple on the page.
+                */
+               temppage = PageGetTempPageCopySpecial(page);
+
+               if (PageAddItem(temppage, (Item) newtup->t_data, newtup->t_len,
+                                       FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+                       elog(PANIC, "sequence_save_tuple: failed to add item to page");
+
+               PageSetLSN(temppage, PageGetLSN(page));
+
+               START_CRIT_SECTION();
+
+               PageRestoreTempPage(temppage, page);
+               seqh->tup.t_data = newtup->t_data;
+               seqh->tup.t_len = newtup->t_len;
+               MarkBufferDirtyHint(seqh->buf, true);
+
+               if (do_wal)
+                       log_sequence_tuple(seqh->rel, newtup, seqh->buf, page);
+
+               END_CRIT_SECTION();
+       }
+       else
+       {
+               /*
+                * New tuple was not sent, so the original tuple was probably just
+                * changed inline, all we need to do is mark the buffer dirty and
+                * optionally log the updated tuple.
+                */
+               START_CRIT_SECTION();
+
+               MarkBufferDirtyHint(seqh->buf, true);
+
+               if (do_wal)
+                       log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+
+               END_CRIT_SECTION();
+       }
+}
+
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+       /* Remove the tuple from cache */
+       if (seqh->tup.t_data != NULL)
+       {
+               seqh->tup.t_data = NULL;
+               seqh->tup.t_len = 0;
+       }
+
+       /* Release the page lock */
+       if (BufferIsValid(seqh->buf))
+       {
+               UnlockReleaseBuffer(seqh->buf);
+               seqh->buf = InvalidBuffer;
+       }
+}
+
+/*
+ * Returns true if sequence was not WAL logged since checkpoint
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+       Page            page;
+       XLogRecPtr      redoptr = GetRedoRecPtr();
+
+       Assert(BufferIsValid(seqh->buf));
+
+       page = BufferGetPage(seqh->buf);
+
+       return (PageGetLSN(page) <= redoptr);
+}
+
+/*
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
  * and store the values into appropriate fields of *new.  Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
  *
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
+ *
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
  */
 static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
                        Form_pg_sequence new, List **owned_by)
 {
        DefElem    *start_value = NULL;
@@ -1159,13 +1154,13 @@ init_params(List *options, bool isInit,
        DefElem    *min_value = NULL;
        DefElem    *cache_value = NULL;
        DefElem    *is_cycled = NULL;
-       ListCell   *option;
+       ListCell   *param;
 
        *owned_by = NIL;
 
-       foreach(option, options)
+       foreach(param, params)
        {
-               DefElem    *defel = (DefElem *) lfirst(option);
+               DefElem    *defel = (DefElem *) lfirst(param);
 
                if (strcmp(defel->defname, "increment") == 0)
                {
@@ -1236,13 +1231,6 @@ init_params(List *options, bool isInit,
                                 defel->defname);
        }
 
-       /*
-        * We must reset log_cnt when isInit or when changing any parameters that
-        * would affect future nextval allocations.
-        */
-       if (isInit)
-               new->log_cnt = 0;
-
        /* INCREMENT BY */
        if (increment_by != NULL)
        {
@@ -1251,7 +1239,6 @@ init_params(List *options, bool isInit,
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("INCREMENT must not be zero")));
-               new->log_cnt = 0;
        }
        else if (isInit)
                new->increment_by = 1;
@@ -1261,7 +1248,6 @@ init_params(List *options, bool isInit,
        {
                new->is_cycled = intVal(is_cycled->arg);
                Assert(BoolIsValid(new->is_cycled));
-               new->log_cnt = 0;
        }
        else if (isInit)
                new->is_cycled = false;
@@ -1270,7 +1256,6 @@ init_params(List *options, bool isInit,
        if (max_value != NULL && max_value->arg)
        {
                new->max_value = defGetInt64(max_value);
-               new->log_cnt = 0;
        }
        else if (isInit || max_value != NULL)
        {
@@ -1278,14 +1263,12 @@ init_params(List *options, bool isInit,
                        new->max_value = SEQ_MAXVALUE;          /* ascending seq */
                else
                        new->max_value = -1;    /* descending seq */
-               new->log_cnt = 0;
        }
 
        /* MINVALUE (null arg means NO MINVALUE) */
        if (min_value != NULL && min_value->arg)
        {
                new->min_value = defGetInt64(min_value);
-               new->log_cnt = 0;
        }
        else if (isInit || min_value != NULL)
        {
@@ -1293,7 +1276,6 @@ init_params(List *options, bool isInit,
                        new->min_value = 1; /* ascending seq */
                else
                        new->min_value = SEQ_MINVALUE;          /* descending seq */
-               new->log_cnt = 0;
        }
 
        /* crosscheck min/max */
@@ -1347,48 +1329,6 @@ init_params(List *options, bool isInit,
                                         bufs, bufm)));
        }
 
-       /* RESTART [WITH] */
-       if (restart_value != NULL)
-       {
-               if (restart_value->arg != NULL)
-                       new->last_value = defGetInt64(restart_value);
-               else
-                       new->last_value = new->start_value;
-               new->is_called = false;
-               new->log_cnt = 0;
-       }
-       else if (isInit)
-       {
-               new->last_value = new->start_value;
-               new->is_called = false;
-       }
-
-       /* crosscheck RESTART (or current value, if changing MIN/MAX) */
-       if (new->last_value < new->min_value)
-       {
-               char            bufs[100],
-                                       bufm[100];
-
-               snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-               snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                          errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
-                                         bufs, bufm)));
-       }
-       if (new->last_value > new->max_value)
-       {
-               char            bufs[100],
-                                       bufm[100];
-
-               snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
-               snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                       errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
-                                  bufs, bufm)));
-       }
-
        /* CACHE */
        if (cache_value != NULL)
        {
@@ -1403,14 +1343,13 @@ init_params(List *options, bool isInit,
                                         errmsg("CACHE (%s) must be greater than zero",
                                                        buf)));
                }
-               new->log_cnt = 0;
        }
        else if (isInit)
                new->cache_value = 1;
 }
 
 /*
- * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ * Process an OWNED BY param for CREATE/ALTER SEQUENCE
  *
  * Ownership permissions on the sequence are already checked,
  * but if we are establishing a new owned-by dependency, we must
@@ -1426,8 +1365,7 @@ process_owned_by(Relation seqrel, List *owned_by)
 
        nnames = list_length(owned_by);
        Assert(nnames > 0);
-       if (nnames == 1)
-       {
+       if (nnames == 1)        {
                /* Must be OWNED BY NONE */
                if (strcmp(strVal(linitial(owned_by)), "none") != 0)
                        ereport(ERROR,
@@ -1514,20 +1452,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
        TupleDesc       tupdesc;
        Datum           values[5];
        bool            isnull[5];
-       SeqTable        elm;
-       Relation        seqrel;
-       Buffer          buf;
-       HeapTupleData seqtuple;
        Form_pg_sequence seq;
+       SequenceHandle  seqh;
 
        /* open and AccessShareLock sequence */
-       init_sequence(relid, &elm, &seqrel);
+       sequence_open(relid, &seqh);
 
        if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("permission denied for sequence %s",
-                                               RelationGetRelationName(seqrel))));
+                                               RelationGetRelationName(seqh.rel))));
 
        tupdesc = CreateTemplateTupleDesc(5, false);
        TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1545,7 +1480,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
        memset(isnull, 0, sizeof(isnull));
 
-       seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+       seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
 
        values[0] = Int64GetDatum(seq->start_value);
        values[1] = Int64GetDatum(seq->min_value);
@@ -1553,12 +1488,184 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
        values[3] = Int64GetDatum(seq->increment_by);
        values[4] = BoolGetDatum(seq->is_cycled);
 
-       UnlockReleaseBuffer(buf);
-       relation_close(seqrel, NoLock);
+       sequence_close(&seqh);
 
        return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       ArrayType  *result;
+       int                     count;
+       char      **keys;
+       char      **values;
+       int                     i;
+       Datum      *out_datums;
+       bool       *out_nulls;
+       int                     dims[1];
+       int                     lbs[1];
+       SequenceHandle seqh;
+
+       sequence_open(relid, &seqh);
+
+       count = seqam_get_state(seqh.rel, &seqh, &keys, &values);
+
+       sequence_close(&seqh);
+
+       out_datums = palloc(count * 2 * sizeof(Datum));
+       out_nulls = palloc(count * 2 * sizeof(bool));
+
+       for (i = 0; i < count; i++)
+       {
+               text       *key = cstring_to_text(keys[i]);
+
+               out_datums[i * 2] = PointerGetDatum(key);
+               out_nulls[i * 2] = false;
+
+               if (values[i] == NULL)
+               {
+                       out_datums[i * 2 + 1] = (Datum) 0;
+                       out_nulls[i * 2 + 1] = true;
+               }
+               else
+               {
+                       text       *value = cstring_to_text(values[i]);
+
+                       out_datums[i * 2 + 1] = PointerGetDatum(value);
+                       out_nulls[i * 2 + 1] = false;
+               }
+       }
+
+       dims[0] = count * 2;
+       lbs[0] = 1;
+
+       result = construct_md_array(out_datums, out_nulls,
+                                                               1, dims, lbs,
+                                                               TEXTOID, -1, false, 'i');
+
+       PG_RETURN_POINTER(result);
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       ArrayType  *in_keyvals = PG_GETARG_ARRAYTYPE_P(1);
+       Datum      *in_datums;
+       bool       *in_nulls;
+       int                     in_count;
+       int                     count;
+       int                     i;
+       char      **keys;
+       char      **values;
+       SequenceHandle seqh;
+
+       Assert(ARR_ELEMTYPE(in_keyvals) == TEXTOID);
+
+       /*
+        * Do the input checks.
+        */
+       if (ARR_NDIM(in_keyvals) != 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                                errmsg("state must be one dimensional array")));
+
+       if ((ARR_DIMS(in_keyvals)[0]) % 2)
+               ereport(ERROR,
+                               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                                errmsg("state array must have even number of elements")));
+
+       deconstruct_array(in_keyvals,
+                                         TEXTOID, -1, false, 'i',
+                                         &in_datums, &in_nulls, &in_count);
+
+       count = in_count / 2;
+       keys = palloc(count * sizeof(char *));
+       values = palloc(count * sizeof(char *));
+
+       for (i = 0; i < count; i++)
+       {
+               if (in_nulls[i * 2])
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                        errmsg("null value not allowed for state array key")));
+
+               keys[i] = text_to_cstring(DatumGetTextP(in_datums[i * 2]));
+
+               if (in_nulls[i * 2 + 1])
+                       values[i] = NULL;
+               else
+                       values[i] = text_to_cstring(DatumGetTextP(in_datums[i * 2 + 1]));
+       }
+
+       /* Call in the sequence. */
+       sequence_open(relid, &seqh);
+
+       seqam_set_state(seqh.rel, &seqh, keys, values, count);
+
+       sequence_close(&seqh);
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * Update pg_class row for sequence to record change in relam.
+ *
+ * Call only while holding AccessExclusiveLock on sequence.
+ *
+ * Note that this is a transactional update of pg_class, rather
+ * than a non-transactional update of the tuple in the sequence's
+ * heap, as occurs elsewhere in this module.
+ */
+static void
+seqrel_update_relam(Oid seqoid, Oid seqamid)
+{
+       Relation        rd;
+       HeapTuple       ctup;
+       Form_pg_class pgcform;
+
+       rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+       /* Fetch a copy of the tuple to scribble on */
+       ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid));
+       if (!HeapTupleIsValid(ctup))
+               elog(ERROR, "pg_class entry for sequence %u unavailable",
+                                               seqoid);
+       pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+       if (pgcform->relam != seqamid)
+       {
+               pgcform->relam = seqamid;
+               simple_heap_update(rd, &ctup->t_self, ctup);
+               CatalogUpdateIndexes(rd, ctup);
+       }
+
+       heap_freetuple(ctup);
+       heap_close(rd, RowExclusiveLock);
+       CommandCounterIncrement();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+                                  Buffer buf, Page page)
+{
+       xl_seq_rec      xlrec;
+       XLogRecPtr      recptr;
+
+       XLogBeginInsert();
+       XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+       xlrec.node = seqrel->rd_node;
+
+       XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+       XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+       recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+       PageSetLSN(page, recptr);
+}
 
 void
 seq_redo(XLogReaderState *record)
@@ -1610,6 +1717,7 @@ seq_redo(XLogReaderState *record)
        pfree(localpage);
 }
 
+
 /*
  * Flush cached sequence information.
  */
@@ -1624,3 +1732,161 @@ ResetSequenceCaches(void)
 
        last_used_seq = NULL;
 }
+
+static Oid
+get_new_seqam_oid(Oid oldAM, char *accessMethod)
+{
+
+       if (oldAM && accessMethod == NULL)
+               return oldAM;
+       else if (accessMethod == NULL)
+               return LOCAL_SEQAM_OID;
+       else
+               return get_seqam_oid(accessMethod, false);
+}
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+                                  int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+       int64 next = *value;
+       int64 rescnt = 0;
+
+       while (incnum)
+       {
+               /*
+                * Check MAXVALUE for ascending sequences and MINVALUE for descending
+                * sequences
+                */
+               if (incby > 0)
+               {
+                       /* ascending sequence */
+                       if ((maxv >= 0 && next > maxv - incby) ||
+                               (maxv < 0 && next + incby > maxv))
+                       {
+                               /*
+                                * We were asked to not report errors, return without incrementing
+                                * and let the caller handle it.
+                                */
+                               if (!report_errors)
+                                       return rescnt;
+                               if (!is_cycled)
+                               {
+                                       char            buf[100];
+
+                                       snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+                                       ereport(ERROR,
+                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                                  errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+                                                                 RelationGetRelationName(seqrel), buf)));
+                               }
+                               next = minv;
+                       }
+                       else
+                               next += incby;
+               }
+               else
+               {
+                       /* descending sequence */
+                       if ((minv < 0 && next < minv - incby) ||
+                               (minv >= 0 && next + incby < minv))
+                       {
+                               /*
+                                * We were asked to not report errors, return without incrementing
+                                * and let the caller handle it.
+                                */
+                               if (!report_errors)
+                                       return rescnt;
+                               if (!is_cycled)
+                               {
+                                       char            buf[100];
+
+                                       snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+                                       ereport(ERROR,
+                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                                  errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+                                                                 RelationGetRelationName(seqrel), buf)));
+                               }
+                               next = maxv;
+                       }
+                       else
+                               next += incby;
+               }
+               rescnt++;
+               incnum--;
+       }
+
+       *value = next;
+
+       return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value)
+{
+       if (value < min_value)
+       {
+               char            bufs[100],
+                                       bufm[100];
+
+               snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+               snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
+                                               bufs, bufm)));
+       }
+
+       if (value > max_value)
+       {
+               char            bufs[100],
+                                       bufm[100];
+
+               snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+               snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                         errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
+                                        bufs, bufm)));
+       }
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+       ListCell *opt;
+
+       foreach(opt, options)
+       {
+               DefElem    *defel = (DefElem *) lfirst(opt);
+
+               if (strcmp(defel->defname, "restart") == 0)
+               {
+                       *found = true;
+                       if (defel->arg != NULL)
+                               return defGetInt64(defel);
+                       else
+                               return default_value;
+               }
+       }
+
+       *found = false;
+       return default_value;
+}
index 06e4332d2ac02b1ad382f489cda7021f219195ab..a6d9996ed9210ed4a4ba1db207c6acd626d5814f 100644 (file)
@@ -9240,7 +9240,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
                        (void) view_reloptions(newOptions, true);
                        break;
                case RELKIND_INDEX:
-                       (void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+                       (void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
+                       break;
+               case RELKIND_SEQUENCE:
+                       (void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
                        break;
                default:
                        ereport(ERROR,
index 029761e74f8d7cbfebb4edbdad53e9f9ad0c508e..16b3be74a9d62e3502c7478bc9a0c9cc08131e15 100644 (file)
@@ -3371,7 +3371,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
 
        COPY_NODE_FIELD(sequence);
        COPY_NODE_FIELD(options);
+       COPY_NODE_FIELD(amoptions);
        COPY_SCALAR_FIELD(ownerId);
+       COPY_STRING_FIELD(accessMethod);
        COPY_SCALAR_FIELD(if_not_exists);
 
        return newnode;
@@ -3384,7 +3386,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 
        COPY_NODE_FIELD(sequence);
        COPY_NODE_FIELD(options);
+       COPY_NODE_FIELD(amoptions);
        COPY_SCALAR_FIELD(missing_ok);
+       COPY_STRING_FIELD(accessMethod);
 
        return newnode;
 }
index 190e50ab8c6909d62a7dbb4b40b0c6f53ff48e34..541ca601299c5841ce7c7823488352a4a2f339e2 100644 (file)
@@ -1563,7 +1563,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
 {
        COMPARE_NODE_FIELD(sequence);
        COMPARE_NODE_FIELD(options);
+       COMPARE_NODE_FIELD(amoptions);
        COMPARE_SCALAR_FIELD(ownerId);
+       COMPARE_STRING_FIELD(accessMethod);
        COMPARE_SCALAR_FIELD(if_not_exists);
 
        return true;
@@ -1574,7 +1576,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 {
        COMPARE_NODE_FIELD(sequence);
        COMPARE_NODE_FIELD(options);
+       COMPARE_NODE_FIELD(amoptions);
        COMPARE_SCALAR_FIELD(missing_ok);
+       COMPARE_STRING_FIELD(accessMethod);
 
        return true;
 }
index 5818858a295e6a2be52f162883e020b464cfb578..875a69002f506c85518f41cabe48002ee4fec4eb 100644 (file)
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -3534,7 +3535,33 @@ CreateSeqStmt:
                                        CreateSeqStmt *n = makeNode(CreateSeqStmt);
                                        $4->relpersistence = $2;
                                        n->sequence = $4;
+                                       n->accessMethod = NULL;
                                        n->options = $5;
+                                       n->amoptions = NIL;
+                                       n->ownerId = InvalidOid;
+                                       $$ = (Node *)n;
+                               }
+                       | CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+                               USING access_method
+                               {
+                                       CreateSeqStmt *n = makeNode(CreateSeqStmt);
+                                       $4->relpersistence = $2;
+                                       n->sequence = $4;
+                                       n->accessMethod = $7;
+                                       n->options = $5;
+                                       n->amoptions = NIL;
+                                       n->ownerId = InvalidOid;
+                                       $$ = (Node *)n;
+                               }
+                       | CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+                               USING access_method WITH reloptions
+                               {
+                                       CreateSeqStmt *n = makeNode(CreateSeqStmt);
+                                       $4->relpersistence = $2;
+                                       n->sequence = $4;
+                                       n->accessMethod = $7;
+                                       n->options = $5;
+                                       n->amoptions = $9;
                                        n->ownerId = InvalidOid;
                                        n->if_not_exists = false;
                                        $$ = (Node *)n;
@@ -3556,7 +3583,31 @@ AlterSeqStmt:
                                {
                                        AlterSeqStmt *n = makeNode(AlterSeqStmt);
                                        n->sequence = $3;
+                                       n->accessMethod = NULL;
+                                       n->options = $4;
+                                       n->amoptions = NIL;
+                                       n->missing_ok = false;
+                                       $$ = (Node *)n;
+                               }
+                       | ALTER SEQUENCE qualified_name OptSeqOptList
+                               USING access_method
+                               {
+                                       AlterSeqStmt *n = makeNode(AlterSeqStmt);
+                                       n->sequence = $3;
+                                       n->accessMethod = $6;
                                        n->options = $4;
+                                       n->amoptions = NIL;
+                                       n->missing_ok = false;
+                                       $$ = (Node *)n;
+                               }
+                       | ALTER SEQUENCE qualified_name OptSeqOptList
+                               USING access_method WITH reloptions
+                               {
+                                       AlterSeqStmt *n = makeNode(AlterSeqStmt);
+                                       n->sequence = $3;
+                                       n->accessMethod = $6;
+                                       n->options = $4;
+                                       n->amoptions = $8;
                                        n->missing_ok = false;
                                        $$ = (Node *)n;
                                }
@@ -3564,11 +3615,34 @@ AlterSeqStmt:
                                {
                                        AlterSeqStmt *n = makeNode(AlterSeqStmt);
                                        n->sequence = $5;
+                                       n->accessMethod = NULL;
                                        n->options = $6;
+                                       n->amoptions = NIL;
+                                       n->missing_ok = true;
+                                       $$ = (Node *)n;
+                               }
+                       | ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+                               USING access_method
+                               {
+                                       AlterSeqStmt *n = makeNode(AlterSeqStmt);
+                                       n->sequence = $5;
+                                       n->accessMethod = $8;
+                                       n->options = $6;
+                                       n->amoptions = NIL;
+                                       n->missing_ok = true;
+                                       $$ = (Node *)n;
+                               }
+                       | ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+                               USING access_method WITH reloptions
+                               {
+                                       AlterSeqStmt *n = makeNode(AlterSeqStmt);
+                                       n->sequence = $5;
+                                       n->accessMethod = $8;
+                                       n->options = $6;
+                                       n->amoptions = $10;
                                        n->missing_ok = true;
                                        $$ = (Node *)n;
                                }
-
                ;
 
 OptSeqOptList: SeqOptList                                                      { $$ = $1; }
@@ -3627,7 +3701,7 @@ SeqOptElem: CACHE NumericOnly
                                {
                                        $$ = makeDefElem("restart", (Node *)$3);
                                }
-               ;
+                       ;
 
 opt_by:                BY                              {}
                        | /* empty */   {}
index 1fc8c2cbe1eac77eb05094784867f1a4272a67a0..dce6566ee893336074a2eabf1ad1f24b9cb9eee2 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -404,6 +405,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
                seqstmt = makeNode(CreateSeqStmt);
                seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
                seqstmt->options = NIL;
+               seqstmt->amoptions = NIL;
+               seqstmt->accessMethod = serial_seqam;
 
                /*
                 * If this is ALTER ADD COLUMN, make sure the sequence will be owned
index 1af43c6de677e2cc6738389a62b36c27d921211d..ccb97913bea793c422165e29d59797cc77d7bcab 100644 (file)
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
 
                case AMOID:
                case AMNAME:
+               case SEQAMOID:
+               case SEQAMNAME:
 
                        /*
-                        * Always do heap scans in pg_am, because it's so small there's
-                        * not much point in an indexscan anyway.  We *must* do this when
+                        * Always do heap scans in pg_am and pg_seqam, because they are
+                        * too small to benefit from an indexscan.  We *must* do this when
                         * initially building critical relcache entries, but we might as
                         * well just always do it.
                         */
index e745006b73bbea37a5ececf296dbb836d4159dbf..661389b0d993faf00284c97b5c495e5c29c41500 100644 (file)
@@ -51,6 +51,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
 static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
@@ -1056,11 +1058,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
        else
                relation->rd_rsdesc = NULL;
 
-       /*
-        * if it's an index, initialize index-related information
-        */
-       if (OidIsValid(relation->rd_rel->relam))
+       /* if it's an index, initialize index-related information */
+       if (relation->rd_rel->relkind == RELKIND_INDEX &&
+               OidIsValid(relation->rd_rel->relam))
                RelationInitIndexAccessInfo(relation);
+       /* same for sequences */
+       else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+                        OidIsValid(relation->rd_rel->relam))
+               RelationInitSequenceAccessInfo(relation);
 
        /* extract reloptions if any */
        RelationParseRelOptions(relation, pg_class_tuple);
@@ -1535,6 +1540,39 @@ LookupOpclassInfo(Oid operatorClassOid,
        return opcentry;
 }
 
+/*
+ * Initialize sequence-access-method support data for an index relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+       HeapTuple               amtuple;
+       MemoryContext   indexcxt;
+       Form_pg_seqam   amform;
+
+       indexcxt = AllocSetContextCreate(CacheMemoryContext,
+                                                                        RelationGetRelationName(rel),
+                                                                        ALLOCSET_SMALL_MINSIZE,
+                                                                        ALLOCSET_SMALL_INITSIZE,
+                                                                        ALLOCSET_SMALL_MAXSIZE);
+       rel->rd_indexcxt = indexcxt;
+
+       rel->rd_aminfo = (RelationAmInfo *)
+               MemoryContextAllocZero(rel->rd_indexcxt,
+                                                          sizeof(RelationAmInfo));
+
+       /*
+        * Make a copy of the pg_am entry for the sequence's access method
+        */
+       amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+       if (!HeapTupleIsValid(amtuple))
+               elog(ERROR, "cache lookup failed for access method %u",
+                        rel->rd_rel->relam);
+       amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform));
+       memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+       ReleaseSysCache(amtuple);
+       rel->rd_seqam = amform;
+}
 
 /*
  *             formrdesc
@@ -4823,6 +4861,22 @@ load_relcache_init_file(bool shared)
                        rel->rd_supportinfo = (FmgrInfo *)
                                MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
                }
+               else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+               {
+                       MemoryContext indexcxt;
+                       Assert(!rel->rd_isnailed);
+                       Assert(false);
+
+                       indexcxt = AllocSetContextCreate(CacheMemoryContext,
+                                                                                        RelationGetRelationName(rel),
+                                                                                        ALLOCSET_SMALL_MINSIZE,
+                                                                                        ALLOCSET_SMALL_INITSIZE,
+                                                                                        ALLOCSET_SMALL_MAXSIZE);
+                       rel->rd_indexcxt = indexcxt;
+                       /* set up zeroed fmgr-info vectors */
+                       rel->rd_aminfo = (RelationAmInfo *)
+                               MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+               }
                else
                {
                        /* Count nailed rels to ensure we have 'em all */
index bd271680e5033fe27ffe9e61e042e1604373511b..5341a09537ae6007e74e3adc350185864e52bef3 100644 (file)
@@ -54,6 +54,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_ts_config.h"
@@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = {
                },
                8
        },
+       {SeqAccessMethodRelationId,     /* SEQAMNAME */
+               SeqAMNameIndexId,
+               1,
+               {
+                       Anum_pg_seqam_seqamname,
+                       0,
+                       0,
+                       0
+               },
+               4
+       },
+       {SeqAccessMethodRelationId,     /* SEQAMOID */
+               SeqAMOidIndexId,
+               1,
+               {
+                       ObjectIdAttributeNumber,
+                       0,
+                       0,
+                       0
+               },
+               4
+       },
        {StatisticRelationId,           /* STATRELATTINH */
                StatisticRelidAttnumInhIndexId,
                3,
index f43aff2d2cb2794a3cfce15c72759dd01ea69d73..1d31ceaef50ecdb16d7ec9e99f3f6cf0a57701b9 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "access/commit_ts.h"
 #include "access/gin.h"
+#include "access/seqam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -2893,6 +2894,17 @@ static struct config_string ConfigureNamesString[] =
                check_default_tablespace, NULL, NULL
        },
 
+       {
+               {"serial_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+                       gettext_noop("Sets the default sequence access method for SERIAL and BIGSERIAL column types."),
+                       gettext_noop("Defaults to 'local' sequence access method."),
+                       GUC_IS_NAME
+               },
+               &serial_seqam,
+               "local",
+               check_serial_seqam, NULL, NULL
+       },
+
        {
                {"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
                        gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
index 110983f176417cc3de91456b3ed48fe6ff7c8c20..a90637b05b14e97d1eeb0ea5e3e3ccbc75eca0e8 100644 (file)
 #default_tablespace = ''               # a tablespace name, '' uses the default
 #temp_tablespaces = ''                 # a list of tablespace names, '' uses
                                        # only default tablespace
+#serial_sequenceam = 'local'   # default sequence access method for SERIAL
 #check_function_bodies = on
 #default_transaction_isolation = 'read committed'
 #default_transaction_read_only = off
index fe08c1b15d2e96ef26bf46d4b9a381317d650f66..6d52da687483dcfea79cd471e450f7db27c601b9 100644 (file)
@@ -51,6 +51,7 @@
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_seqam.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "libpq/libpq-fs.h"
@@ -4533,6 +4534,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
        int                     i_relreplident;
        int                     i_owning_tab;
        int                     i_owning_col;
+       int                     i_relam;
        int                     i_reltablespace;
        int                     i_reloptions;
        int                     i_checkoption;
@@ -4584,6 +4586,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
+                                                 "c.relam, "
                                                  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
                                                  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
                                                  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4625,6 +4628,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
+                                                 "c.relam, "
                                                  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
                                                  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
                                                  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4666,6 +4670,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
+                                                 "c.relam, "
                                                  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
                                                  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
                                                  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4707,6 +4712,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
+                                                 "c.relam, "
                                                  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
                                                "array_to_string(c.reloptions, ', ') AS reloptions, "
                                                  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4746,6 +4752,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
+                                                 "c.relam, "
                                                  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
                                                "array_to_string(c.reloptions, ', ') AS reloptions, "
                                                  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4784,6 +4791,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "NULL AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
+                                                 "c.relam, "
                                                  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
                                                "array_to_string(c.reloptions, ', ') AS reloptions, "
                                                  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4822,6 +4830,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "NULL AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
+                                                 "c.relam, "
                                                  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
                                                "array_to_string(c.reloptions, ', ') AS reloptions, "
                                                  "NULL AS toast_reloptions "
@@ -4860,6 +4869,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "NULL AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
+                                                 "c.relam, "
                                                  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
                                                  "NULL AS reloptions, "
                                                  "NULL AS toast_reloptions "
@@ -4897,6 +4907,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "NULL AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
+                                                 "c.relam, "
                                                  "NULL AS reltablespace, "
                                                  "NULL AS reloptions, "
                                                  "NULL AS toast_reloptions "
@@ -4930,6 +4941,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "NULL AS reloftype, "
                                                  "NULL::oid AS owning_tab, "
                                                  "NULL::int4 AS owning_col, "
+                                                 "c.relam, "
                                                  "NULL AS reltablespace, "
                                                  "NULL AS reloptions, "
                                                  "NULL AS toast_reloptions "
@@ -4958,6 +4970,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "NULL AS reloftype, "
                                                  "NULL::oid AS owning_tab, "
                                                  "NULL::int4 AS owning_col, "
+                                                 "c.relam, "
                                                  "NULL AS reltablespace, "
                                                  "NULL AS reloptions, "
                                                  "NULL AS toast_reloptions "
@@ -4996,6 +5009,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                                                  "NULL AS reloftype, "
                                                  "NULL::oid AS owning_tab, "
                                                  "NULL::int4 AS owning_col, "
+                                                 "c.relam, "
                                                  "NULL AS reltablespace, "
                                                  "NULL AS reloptions, "
                                                  "NULL AS toast_reloptions "
@@ -5048,6 +5062,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
        i_relpages = PQfnumber(res, "relpages");
        i_owning_tab = PQfnumber(res, "owning_tab");
        i_owning_col = PQfnumber(res, "owning_col");
+       i_relam = PQfnumber(res, "relam");
        i_reltablespace = PQfnumber(res, "reltablespace");
        i_reloptions = PQfnumber(res, "reloptions");
        i_checkoption = PQfnumber(res, "checkoption");
@@ -5112,6 +5127,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
                        tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
                        tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
                }
+               if (PQgetisnull(res, i, i_relam))
+                       tblinfo[i].relam = InvalidOid;
+               else
+                       tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
                tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
                tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
                if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -14534,7 +14553,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
                           *incby,
                           *maxv = NULL,
                           *minv = NULL,
-                          *cache;
+                          *cache,
+                          *amname = NULL;
        char            bufm[100],
                                bufx[100];
        bool            cycled;
@@ -14613,6 +14633,37 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
        cache = PQgetvalue(res, 0, 5);
        cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
+       /*
+        * 9.5 adds sequence access methods but we only care if valid
+        * sequence am that is not the default one is specified.
+        */
+       if (fout->remoteVersion >= 90500 &&
+               tbinfo->relam != InvalidOid &&
+               tbinfo->relam != LOCAL_SEQAM_OID)
+       {
+               PGresult   *res2;
+
+               printfPQExpBuffer(query, "SELECT a.seqamname\n"
+                                                                "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+                                                                "WHERE c.relam = a.oid AND c.oid = %u",
+                                                 tbinfo->dobj.catId.oid);
+
+               res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+               if (PQntuples(res2) != 1)
+               {
+                       write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+                                                                        "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+                                                                        PQntuples(res2)),
+                                         tbinfo->dobj.name, PQntuples(res2));
+                       exit_nicely(1);
+               }
+
+               amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+               PQclear(res2);
+       }
+
        /*
         * DROP must be fully qualified in case same name appears in pg_catalog
         */
@@ -14654,6 +14705,13 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
                                          "    CACHE %s%s",
                                          cache, (cycled ? "\n    CYCLE" : ""));
 
+       /*
+        * Only produce using when it makes sense,
+        * this helps with backwards compatibility.
+        */
+       if (amname)
+               appendPQExpBuffer(query, "\n    USING %s", fmtId(amname));
+
        appendPQExpBufferStr(query, ";\n");
 
        appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14720,6 +14778,9 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
                                 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
                                 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
+       if (amname)
+               free(amname);
+
        PQclear(res);
 
        destroyPQExpBuffer(query);
@@ -14736,16 +14797,26 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 {
        TableInfo  *tbinfo = tdinfo->tdtable;
        PGresult   *res;
-       char       *last;
-       bool            called;
        PQExpBuffer query = createPQExpBuffer();
 
        /* Make sure we are in proper schema */
        selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
 
-       appendPQExpBuffer(query,
-                                         "SELECT last_value, is_called FROM %s",
-                                         fmtId(tbinfo->dobj.name));
+       /*
+        * On 9.5 there is special interface for dumping sequences but we only
+        * use it for one with nondefault access method because we can produce
+        * more backward compatible dump that way.
+        */
+       if (fout->remoteVersion >= 90500 &&
+               tbinfo->relam != InvalidOid &&
+               tbinfo->relam != LOCAL_SEQAM_OID)
+               appendPQExpBuffer(query,
+                                                 "SELECT quote_literal(pg_catalog.pg_sequence_get_state(%s))",
+                                                 fmtId(tbinfo->dobj.name));
+       else
+               appendPQExpBuffer(query,
+                                                 "SELECT last_value, is_called FROM %s",
+                                                 fmtId(tbinfo->dobj.name));
 
        res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -14758,14 +14829,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
                exit_nicely(1);
        }
 
-       last = PQgetvalue(res, 0, 0);
-       called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
        resetPQExpBuffer(query);
-       appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
-       appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
-       appendPQExpBuffer(query, ", %s, %s);\n",
-                                         last, (called ? "true" : "false"));
+
+       if (fout->remoteVersion >= 90500 &&
+               tbinfo->relam != InvalidOid &&
+               tbinfo->relam != LOCAL_SEQAM_OID)
+       {
+               char   *state = PQgetvalue(res, 0, 0);
+
+               appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+               appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+               /* The state got quote in the SELECT. */
+               appendPQExpBuffer(query, ", %s);\n", state);
+       }
+       else
+       {
+               char   *last = PQgetvalue(res, 0, 0);
+               bool    called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+               appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+               appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+               appendPQExpBuffer(query, ", %s, %s);\n",
+                                                 last, (called ? "true" : "false"));
+       }
 
        ArchiveEntry(fout, nilCatalogId, createDumpId(),
                                 tbinfo->dobj.name,
index a9d3c1016be7083a05657e79fa55714d83360e17..7eaee3260371df5d22f3b629bb2227adb83c2967 100644 (file)
@@ -221,6 +221,7 @@ typedef struct _tableInfo
        /* these two are set only if table is a sequence owned by a column: */
        Oid                     owning_tab;             /* OID of table owning sequence */
        int                     owning_col;             /* attr # of column owning sequence */
+       int                     relam;                  /* access method (from pg_clas) */
        int                     relpages;               /* table's size in pages (from pg_class) */
 
        bool            interesting;    /* true if need to collect more data */
index 04d769e3d6bd2a5f557da115a17938551e0fdb8f..a01f319bcc92b004522285a6d6b71b8e9c5d1911 100644 (file)
@@ -1400,30 +1400,6 @@ describeOneTableDetails(const char *schemaname,
        PQclear(res);
        res = NULL;
 
-       /*
-        * If it's a sequence, fetch its values and store into an array that will
-        * be used later.
-        */
-       if (tableinfo.relkind == 'S')
-       {
-               printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
-               /* must be separate because fmtId isn't reentrant */
-               appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
-               res = PSQLexec(buf.data);
-               if (!res)
-                       goto error_return;
-
-               seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
-               for (i = 0; i < PQnfields(res); i++)
-                       seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
-               seq_values[i] = NULL;
-
-               PQclear(res);
-               res = NULL;
-       }
-
        /*
         * Get column info
         *
@@ -1468,13 +1444,55 @@ describeOneTableDetails(const char *schemaname,
 
        appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
        appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+       /*
+        * For sequence, fetch only the common column unless verbose was specified.
+        * Note that this is change from pre9.5 versions.
+        */
+       if (tableinfo.relkind == 'S')
+               appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
        appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
 
+
        res = PSQLexec(buf.data);
        if (!res)
                goto error_return;
        numrows = PQntuples(res);
 
+       /*
+        * If it's a sequence, fetch its values and store into an array that will
+        * be used later.
+        */
+       if (tableinfo.relkind == 'S')
+       {
+               PGresult   *result;
+
+               /*
+                * Use column names from the column info query, to automatically skip
+                * unwanted columns.
+                */
+               printfPQExpBuffer(&buf, "SELECT ");
+               for (i = 0; i < numrows; i++)
+                       appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+               appendPQExpBuffer(&buf, " FROM %s",
+                                                 fmtId(schemaname));
+               /* must be separate because fmtId isn't reentrant */
+               appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+               result = PSQLexec(buf.data);
+               if (!result)
+                       goto error_return;
+
+               seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+               for (i = 0; i < PQnfields(result); i++)
+                       seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+               seq_values[i] = NULL;
+
+               PQclear(result);
+       }
+
        /* Make title */
        switch (tableinfo.relkind)
        {
@@ -1803,6 +1821,8 @@ describeOneTableDetails(const char *schemaname,
                                                  oid);
 
                result = PSQLexec(buf.data);
+
+               /* Same logic as above, only print result when we get one row. */
                if (!result)
                        goto error_return;
                else if (PQntuples(result) == 1)
@@ -1812,12 +1832,56 @@ describeOneTableDetails(const char *schemaname,
                        printTableAddFooter(&cont, buf.data);
                }
 
+               PQclear(result);
+
+               /* Get the Access Method name for the sequence */
+               printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+                                                               "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+                                                               "WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+               result = PSQLexec(buf.data);
+
                /*
                 * If we get no rows back, don't show anything (obviously). We should
                 * never get more than one row back, but if we do, just ignore it and
                 * don't print anything.
                 */
+               if (!result)
+                       goto error_return;
+               else if (PQntuples(result) == 1)
+               {
+                       printfPQExpBuffer(&buf, _("Access Method: %s"),
+                                                         PQgetvalue(result, 0, 0));
+                       printTableAddFooter(&cont, buf.data);
+               }
+
                PQclear(result);
+
+               if (verbose)
+               {
+                       /* Get the Access Method state */
+                       printfPQExpBuffer(&buf,
+                                                         "SELECT pg_catalog.pg_sequence_get_state('%s');",
+                                                         oid);
+
+                       result = PSQLexec(buf.data);
+
+                       /*
+                        * If we get no rows back, don't show anything (obviously). We should
+                        * never get more than one row back, but if we do, just ignore it and
+                        * don't print anything.
+                        */
+                       if (!result)
+                               goto error_return;
+                       else if (PQntuples(result) == 1)
+                       {
+                               printfPQExpBuffer(&buf, _("Access Method State: %s"),
+                                                                 PQgetvalue(result, 0, 0));
+                               printTableAddFooter(&cont, buf.data);
+                       }
+
+                       PQclear(result);
+               }
        }
        else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
                         tableinfo.relkind == 'f')
index e7b6bb52cad8c96a78628f0c8699fc115814ed0c..4c28c8c5132fe4f8ed6499ce54802062bfa27ba7 100644 (file)
@@ -46,8 +46,9 @@ typedef enum relopt_kind
        RELOPT_KIND_SPGIST = (1 << 8),
        RELOPT_KIND_VIEW = (1 << 9),
        RELOPT_KIND_BRIN = (1 << 10),
+       RELOPT_KIND_SEQUENCE = (1 << 11),
        /* if you add a new kind, make sure you update "last_default" too */
-       RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
+       RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE,
        /* some compilers treat enums as signed ints, so we can't use 1 << 31 */
        RELOPT_KIND_MAX = (1 << 30)
 } relopt_kind;
@@ -270,8 +271,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
                                   relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
-                                bool validate);
+extern bytea *am_reloptions(RegProcedure amoptions, Datum reloptions,
+                                                  bool validate);
 extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644 (file)
index 0000000..e53f1ab
--- /dev/null
@@ -0,0 +1,67 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ *       Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+extern char *serial_seqam;
+
+extern void seqam_init(Oid seqamid, Oid seqrelid, List *seqparams,
+                                          List *reloptions, Datum *values, bool *nulls);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+                                                int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+                                                int64 new_value);
+extern int seqam_get_state(Relation seqrel, SequenceHandle *seqh,
+                                                  char ***keys, char ***values);
+void seqam_set_state(Relation seqrel, SequenceHandle *seqh,
+                                        char **keys, char **values, int count);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_save_tuple(SequenceHandle *seqh, HeapTuple newtup,
+                                                               bool do_wal);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+                                                               int64 minv, int64 maxv, int64 incby,
+                                                               bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+                                                                int64 max_value);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+                                                                               bool *found);
+
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_local_set_state(PG_FUNCTION_ARGS);
+
+#endif   /* SEQAM_H */
index a68022960cff34302d0f3fd1d47270176a48e8cf..09df8c3e4637872a0e02611aa9ab7de8a81cf571 100644 (file)
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
 DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
 #define RewriteRelRulenameIndexId  2693
 
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAMNameIndexId  6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAMOidIndexId  6021
+
 DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
 #define SharedDependDependerIndexId            1232
 DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
index 619d9969cab74d9142112117963269f8ed155ac9..07862e2401e11e45a807952b5bdf837dcd3ccadf 100644 (file)
@@ -1831,6 +1831,10 @@ DATA(insert OID = 1765 (  setval                 PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 20 "
 DESCR("set sequence value and is_called status");
 DATA(insert OID = 3078 (  pg_sequence_parameters       PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3284 (  pg_sequence_get_state                PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 1009 "2205" _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3285 (  pg_sequence_set_state                PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 1009" _null_ _null_ _null_ _null_ pg_sequence_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a sequence");
 
 DATA(insert OID = 1579 (  varbit_in                    PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
 DESCR("I/O");
@@ -5119,6 +5123,20 @@ DESCR("peek at changes from replication slot");
 DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
 DESCR("peek at binary changes from replication slot");
 
+DATA(insert OID = 6023 (  seqam_local_reloptions          PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 (  seqam_local_init        PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "26 2281 17 2281 2281" _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 (  seqam_local_alloc       PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 (  seqam_local_setval      PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 (  seqam_local_get_state           PGNSP PGUID 12 1 0 0 0 f f f f f f s 4 0 23 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ seqam_local_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 (  seqam_local_set_state           PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 2278 "2281 2281 2281 2281 23" _null_ _null_ _null_ _null_ seqam_local_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
 /* event triggers */
 DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects             PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, is_temporary, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
 DESCR("list objects dropped by the current command");
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644 (file)
index 0000000..5c789e3
--- /dev/null
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ *       definition of the system "sequence access method" relation (pg_seqam)
+ *       along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ *             the genbki.pl script reads this file and generates .bki
+ *             information from the DATA() statements.
+ *
+ *             XXX do NOT break up DATA() statements into multiple lines!
+ *                     the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *             pg_seqam definition.  cpp turns this into
+ *             typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId      32
+
+CATALOG(pg_seqam,32)
+{
+       NameData        seqamname;                      /* access method name */
+       regproc         seqamreloptions;        /* parse AM-specific options */
+       regproc         seqaminit;                      /* sequence initialization */
+       regproc         seqamalloc;                     /* get next allocation of range of values function */
+       regproc         seqamsetval;            /* set value function */
+       regproc         seqamgetstate;          /* dump state, used by pg_dump */
+       regproc         seqamsetstate;          /* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ *             Form_pg_seqam corresponds to a pointer to a tuple with
+ *             the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ *             compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam                                         7
+#define Anum_pg_seqam_seqamname                                1
+#define Anum_pg_seqam_seqamreloptions          2
+#define Anum_pg_seqam_seqaminit                                3
+#define Anum_pg_seqam_seqamalloc                       4
+#define Anum_pg_seqam_seqamsetval                      5
+#define Anum_pg_seqam_seqamgetstate                    6
+#define Anum_pg_seqam_seqamsetstate                    7
+
+/* ----------------
+ *             initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 6022 (  local                seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_get_state seqam_local_set_state));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#endif   /* PG_SEQAM_H */
index 44862bba81d050844a735ed0a21f0da1b8d32431..bed8d7d2fca6c45a6a3eba975e69bf2ca632eb8f 100644 (file)
@@ -30,9 +30,9 @@ typedef struct FormData_pg_sequence
        int64           max_value;
        int64           min_value;
        int64           cache_value;
-       int64           log_cnt;
        bool            is_cycled;
        bool            is_called;
+       bytea           amdata;
 } FormData_pg_sequence;
 
 typedef FormData_pg_sequence *Form_pg_sequence;
@@ -48,12 +48,12 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 #define SEQ_COL_MAXVALUE               5
 #define SEQ_COL_MINVALUE               6
 #define SEQ_COL_CACHE                  7
-#define SEQ_COL_LOG                            8
-#define SEQ_COL_CYCLE                  9
-#define SEQ_COL_CALLED                 10
+#define SEQ_COL_CYCLE                  8
+#define SEQ_COL_CALLED                 9
+#define SEQ_COL_AMDATA                 10
 
 #define SEQ_COL_FIRSTCOL               SEQ_COL_NAME
-#define SEQ_COL_LASTCOL                        SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL                        SEQ_COL_AMDATA
 
 /* XLOG stuff */
 #define XLOG_SEQ_LOG                   0x00
@@ -72,6 +72,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
 
 extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
index 0e257ac46ce2104d2d410d6eb347609f5df51d78..44d6ea44a69d8122c1c32cafdb8ecdd7c879d9cc 100644 (file)
@@ -2030,8 +2030,10 @@ typedef struct CreateSeqStmt
 {
        NodeTag         type;
        RangeVar   *sequence;           /* the sequence to create */
-       List       *options;
+       List       *options;        /* standard sequence options */
+       List       *amoptions;          /* am specific options */
        Oid                     ownerId;                /* ID of owner, or InvalidOid for default */
+       char       *accessMethod;   /* USING name of access method (eg. Local) */
        bool            if_not_exists;  /* just do nothing if it already exists? */
 } CreateSeqStmt;
 
@@ -2039,8 +2041,10 @@ typedef struct AlterSeqStmt
 {
        NodeTag         type;
        RangeVar   *sequence;           /* the sequence to alter */
-       List       *options;
+       List       *options;        /* standard sequence options */
+       List       *amoptions;          /* am specific options */
        bool            missing_ok;             /* skip error if a role is missing? */
+       char       *accessMethod;   /* USING name of access method (eg. Local) */
 } AlterSeqStmt;
 
 /* ----------------------
index ff78b70b96deeee6e476a5b3a720bfe0e920a562..1ac3ef73f7b6e4a0899bf52983ebb927be15e4c4 100644 (file)
@@ -410,6 +410,7 @@ extern void GUC_check_errcode(int sqlerrcode);
  */
 
 /* in commands/tablespace.c */
+extern bool check_serial_seqam(char **newval, void **extra, GucSource source);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
index 9e17d87413d7d7855efd7910f803f59b47a2e6cc..62e8a95029d36e1d4740d8f5cfef91f253fba458 100644 (file)
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -48,10 +49,12 @@ typedef LockInfoData *LockInfo;
 
 /*
  * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
+ * functions, defined by the pg_am row associated with an index relation, or the pg_seqam
+ * row associated with a sequence relation.
  */
 typedef struct RelationAmInfo
 {
+       /* pg_am only */
        FmgrInfo        aminsert;
        FmgrInfo        ambeginscan;
        FmgrInfo        amgettuple;
@@ -61,6 +64,14 @@ typedef struct RelationAmInfo
        FmgrInfo        ammarkpos;
        FmgrInfo        amrestrpos;
        FmgrInfo        amcanreturn;
+       FmgrInfo        amcostestimate;
+
+       /* pg_seqam only */
+       FmgrInfo        seqamalloc;
+       FmgrInfo        seqamsetval;
+
+       /* Common */
+       FmgrInfo        amoptions;
 } RelationAmInfo;
 
 
@@ -131,23 +142,25 @@ typedef struct RelationData
        struct HeapTupleData *rd_indextuple;            /* all of pg_index tuple */
        Form_pg_am      rd_am;                  /* pg_am tuple for index's AM */
 
+       Form_pg_seqam rd_seqam;         /* pg_seqam tuple for sequence's AM */
+
        /*
-        * index access support info (used only for an index relation)
+        * Access support info (used only for index or sequence relations)
         *
         * Note: only default support procs for each opclass are cached, namely
         * those with lefttype and righttype equal to the opclass's opcintype. The
         * arrays are indexed by support function number, which is a sufficient
         * identifier given that restriction.
         *
-        * Note: rd_amcache is available for index AMs to cache private data about
-        * an index.  This must be just a cache since it may get reset at any time
+        * Note: rd_amcache is available for AMs to cache private data about
+        * an object.  This must be just a cache since it may get reset at any time
         * (in particular, it will get reset by a relcache inval message for the
         * index).  If used, it must point to a single memory chunk palloc'd in
         * rd_indexcxt.  A relcache reset will include freeing that chunk and
         * setting rd_amcache = NULL.
         */
        MemoryContext rd_indexcxt;      /* private memory cxt for this stuff */
-       RelationAmInfo *rd_aminfo;      /* lookup info for funcs found in pg_am */
+       RelationAmInfo *rd_aminfo;      /* lookup info for funcs found in pg_am or pg_seqam */
        Oid                *rd_opfamily;        /* OIDs of op families for each index col */
        Oid                *rd_opcintype;       /* OIDs of opclass declared input data types */
        RegProcedure *rd_support;       /* OIDs of support procedures */
@@ -158,7 +171,7 @@ typedef struct RelationData
        Oid                *rd_exclops;         /* OIDs of exclusion operators, if any */
        Oid                *rd_exclprocs;       /* OIDs of exclusion ops' procs, if any */
        uint16     *rd_exclstrats;      /* exclusion ops' strategy numbers, if any */
-       void       *rd_amcache;         /* available for use by index AM */
+       void       *rd_amcache;         /* available for use by AM */
        Oid                *rd_indcollation;    /* OIDs of index collations */
 
        /*
index ba0b0907dfb277736221cd0928917724d4b06db9..ac04502840336dc0b7306d57aaa0e09135320f55 100644 (file)
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
        RELNAMENSP,
        RELOID,
        RULERELNAME,
+       SEQAMNAME,
+       SEQAMOID,
        STATRELATTINH,
        TABLESPACEOID,
        TSCONFIGMAP,
index c7be273ae166146fbd44a8f1afc5ba7f89e42f3e..d4e250fa4e0797855a9ee6c2495bc34bf4c5fc8a 100644 (file)
@@ -123,6 +123,7 @@ pg_proc|t
 pg_range|t
 pg_rewrite|t
 pg_seclabel|t
+pg_seqam|t
 pg_shdepend|t
 pg_shdescription|t
 pg_shseclabel|t
index 8783ca62a6f268412d823c50c2d08ca444d9efbb..3982a46653d61fb833798900fee80d0bf42db2df 100644 (file)
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
       33
 (1 row)
 
-SELECT setval('sequence_test'::text, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
 (1 row)
 
 SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
       33
 (1 row)
 
-SELECT setval('sequence_test'::regclass, 99, false);
- setval 
---------
-     99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state 
+-----------------------
 (1 row)
 
 SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       0 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | f         | \x0000000000000000
 (1 row)
 
 SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
 (1 row)
 
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      31 | f         | t
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | is_cycled | is_called |       amdata       
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 | f         | t         | \x1f00000000000000
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
index 9e7ba7247103edc997617cb1fd169476d4730d31..a69a56d28dae5fa6a043b353a591342431f158a2 100644 (file)
@@ -106,9 +106,9 @@ SELECT table_name, column_name, is_updatable
  ro_view19  | max_value     | NO
  ro_view19  | min_value     | NO
  ro_view19  | cache_value   | NO
- ro_view19  | log_cnt       | NO
  ro_view19  | is_cycled     | NO
  ro_view19  | is_called     | NO
+ ro_view19  | amdata        | NO
  ro_view2   | a             | NO
  ro_view2   | b             | NO
  ro_view20  | a             | NO
index 0dd653dc223a6315bb58556de0485089388fa15c..e1273efb44765150a9f9b8e0100f88c331ee62eb 100644 (file)
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
 SELECT currval('sequence_test'::regclass);
 SELECT setval('sequence_test'::text, 32);
 SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::regclass);
 SELECT setval('sequence_test'::regclass, 32);
 SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
 SELECT nextval('sequence_test'::text);
 DISCARD SEQUENCES;
 SELECT currval('sequence_test'::regclass);