0001-seqam-v9.patch
application/x-patch
Filename: 0001-seqam-v9.patch
Type: application/x-patch
Part: 0
Message:
Re: Sequence Access Method WIP
Patch
Format: format-patch
Series: patch v9-0001
Subject: seqam v9
| File | + | − |
|---|---|---|
| doc/src/sgml/catalogs.sgml | 94 | 0 |
| doc/src/sgml/config.sgml | 21 | 0 |
| doc/src/sgml/filelist.sgml | 1 | 0 |
| doc/src/sgml/postgres.sgml | 1 | 0 |
| doc/src/sgml/ref/alter_sequence.sgml | 55 | 0 |
| doc/src/sgml/ref/create_sequence.sgml | 25 | 0 |
| doc/src/sgml/seqam.sgml | 289 | 0 |
| src/backend/access/common/reloptions.c | 8 | 4 |
| src/backend/access/Makefile | 2 | 1 |
| src/backend/access/sequence/Makefile | 17 | 0 |
| src/backend/access/sequence/seqam.c | 308 | 0 |
| src/backend/access/sequence/seqlocal.c | 383 | 0 |
| src/backend/bootstrap/bootparse.y | 1 | 0 |
| src/backend/catalog/heap.c | 15 | 1 |
| src/backend/catalog/Makefile | 1 | 1 |
| src/backend/catalog/objectaddress.c | 1 | 0 |
| src/backend/catalog/toasting.c | 1 | 0 |
| src/backend/commands/cluster.c | 1 | 0 |
| src/backend/commands/createas.c | 2 | 1 |
| src/backend/commands/indexcmds.c | 1 | 1 |
| src/backend/commands/sequence.c | 746 | 499 |
| src/backend/commands/tablecmds.c | 48 | 11 |
| src/backend/commands/typecmds.c | 2 | 1 |
| src/backend/commands/view.c | 2 | 1 |
| src/backend/nodes/copyfuncs.c | 4 | 0 |
| src/backend/nodes/equalfuncs.c | 4 | 0 |
| src/backend/parser/gram.y | 76 | 2 |
| src/backend/parser/parse_utilcmd.c | 3 | 0 |
| src/backend/tcop/utility.c | 4 | 2 |
| src/backend/utils/cache/catcache.c | 4 | 2 |
| src/backend/utils/cache/relcache.c | 53 | 7 |
| src/backend/utils/cache/syscache.c | 23 | 0 |
| src/backend/utils/misc/guc.c | 12 | 0 |
| src/backend/utils/misc/postgresql.conf.sample | 1 | 0 |
| src/bin/pg_dump/pg_dump.c | 102 | 13 |
| src/bin/pg_dump/pg_dump.h | 1 | 0 |
| src/bin/psql/describe.c | 88 | 24 |
| src/include/access/reloptions.h | 2 | 2 |
| src/include/access/seqam.h | 69 | 0 |
| src/include/catalog/heap.h | 1 | 0 |
| src/include/catalog/indexing.h | 5 | 0 |
| src/include/catalog/pg_proc.h | 18 | 0 |
| src/include/catalog/pg_seqam.h | 74 | 0 |
| src/include/commands/sequence.h | 7 | 5 |
| src/include/commands/tablecmds.h | 1 | 1 |
| src/include/nodes/parsenodes.h | 6 | 2 |
| src/include/utils/guc.h | 1 | 0 |
| src/include/utils/rel.h | 18 | 1 |
| src/include/utils/syscache.h | 2 | 0 |
| src/test/regress/expected/sanity_check.out | 1 | 0 |
| src/test/regress/expected/sequence.out | 21 | 14 |
| src/test/regress/expected/updatable_views.out | 1 | 1 |
| src/test/regress/sql/sequence.sql | 8 | 2 |
From 33b883d88f79f5aafa5643931bd6d892344885cd Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Mon, 22 Sep 2014 19:22:40 +0200
Subject: [PATCH 1/3] seqam v9
---
doc/src/sgml/catalogs.sgml | 94 ++
doc/src/sgml/config.sgml | 21 +
doc/src/sgml/filelist.sgml | 1 +
doc/src/sgml/postgres.sgml | 1 +
doc/src/sgml/ref/alter_sequence.sgml | 55 ++
doc/src/sgml/ref/create_sequence.sgml | 25 +
doc/src/sgml/seqam.sgml | 289 ++++++
src/backend/access/Makefile | 3 +-
src/backend/access/common/reloptions.c | 12 +-
src/backend/access/sequence/Makefile | 17 +
src/backend/access/sequence/seqam.c | 308 ++++++
src/backend/access/sequence/seqlocal.c | 383 ++++++++
src/backend/bootstrap/bootparse.y | 1 +
src/backend/catalog/Makefile | 2 +-
src/backend/catalog/heap.c | 16 +-
src/backend/catalog/objectaddress.c | 1 +
src/backend/catalog/toasting.c | 1 +
src/backend/commands/cluster.c | 1 +
src/backend/commands/createas.c | 3 +-
src/backend/commands/indexcmds.c | 2 +-
src/backend/commands/sequence.c | 1245 +++++++++++++++----------
src/backend/commands/tablecmds.c | 59 +-
src/backend/commands/typecmds.c | 3 +-
src/backend/commands/view.c | 3 +-
src/backend/nodes/copyfuncs.c | 4 +
src/backend/nodes/equalfuncs.c | 4 +
src/backend/parser/gram.y | 78 +-
src/backend/parser/parse_utilcmd.c | 3 +
src/backend/tcop/utility.c | 6 +-
src/backend/utils/cache/catcache.c | 6 +-
src/backend/utils/cache/relcache.c | 60 +-
src/backend/utils/cache/syscache.c | 23 +
src/backend/utils/misc/guc.c | 12 +
src/backend/utils/misc/postgresql.conf.sample | 1 +
src/bin/pg_dump/pg_dump.c | 115 ++-
src/bin/pg_dump/pg_dump.h | 1 +
src/bin/psql/describe.c | 112 ++-
src/include/access/reloptions.h | 4 +-
src/include/access/seqam.h | 69 ++
src/include/catalog/heap.h | 1 +
src/include/catalog/indexing.h | 5 +
src/include/catalog/pg_proc.h | 18 +
src/include/catalog/pg_seqam.h | 74 ++
src/include/commands/sequence.h | 12 +-
src/include/commands/tablecmds.h | 2 +-
src/include/nodes/parsenodes.h | 8 +-
src/include/utils/guc.h | 1 +
src/include/utils/rel.h | 19 +-
src/include/utils/syscache.h | 2 +
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/expected/sequence.out | 35 +-
src/test/regress/expected/updatable_views.out | 2 +-
src/test/regress/sql/sequence.sql | 10 +-
53 files changed, 2635 insertions(+), 599 deletions(-)
create mode 100644 doc/src/sgml/seqam.sgml
create mode 100644 src/backend/access/sequence/Makefile
create mode 100644 src/backend/access/sequence/seqam.c
create mode 100644 src/backend/access/sequence/seqlocal.c
create mode 100644 src/include/access/seqam.h
create mode 100644 src/include/catalog/pg_seqam.h
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 898865e..403a223 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -249,6 +249,11 @@
</row>
<row>
+ <entry><link linkend="catalog-pg-seqam"><structname>pg_seqam</structname></link></entry>
+ <entry>sequence access methods</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
<entry>dependencies on shared objects</entry>
</row>
@@ -5536,6 +5541,95 @@
</table>
</sect1>
+ <sect1 id="catalog-pg-seqam">
+ <title><structname>pg_seqam</structname></title>
+
+ <indexterm zone="catalog-pg-seqam">
+ <primary>pg_seqam</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_seqam</structname> stores information about
+ sequence access methods. There is one row for each sequence access method
+ installed on the system.
+ </para>
+
+ <table>
+ <title><structname>pg_seqam</> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+
+ <row>
+ <entry><structfield>oid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry></entry>
+ <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>seqamname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the access method</entry>
+ </row>
+
+ <row>
+ <entry><structfield>seqamreloptions</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Function to parse and validate <structfield>reloptions</> for the access method</entry>
+ </row>
+
+ <row>
+ <entry><structfield>seqaminit</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Function called during initialization or <command>RESET</command> of a sequence</entry>
+ </row>
+
+ <row>
+ <entry><structfield>seqamalloc</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry><quote>Allocate new sequence id</quote> function</entry>
+ </row>
+
+ <row>
+ <entry><structfield>seqamsetval</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Function implementing <function>setval()</function> interface</entry>
+ </row>
+
+ <row>
+ <entry><structfield>seqamgetstate</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Function to dump current state of a sequence (used mainly by pg_dump)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>seqamsetstate</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Function to restore a dumped state of a sequence (used mainly by pg_dump)</entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
<sect1 id="catalog-pg-shdepend">
<title><structname>pg_shdepend</structname></title>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b30c68d..ce40106 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5671,6 +5671,27 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem>
</varlistentry>
+ <varlistentry id="guc-serial-sequenceam" xreflabel="serial_sequenceam">
+ <term><varname>serial_sequenceam</varname> (<type>string</type>)
+ <indexterm>
+ <primary><varname>serial_sequenceam</> configuration parameter</primary>
+ </indexterm>
+ <indexterm><primary>sequence access method</><secondary>serial</></>
+ </term>
+ <listitem>
+ <para>
+ This variable specifies the default sequence access method to be used
+ for <type>SERIAL</> and <type>BIGSERIAL</>.
+ </para>
+
+ <para>
+ The default is 'local' sequence access method. If the value does not
+ match the name of any existing sequence access method, an error will be
+ raised.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-check-function-bodies" xreflabel="check_function_bodies">
<term><varname>check_function_bodies</varname> (<type>boolean</type>)
<indexterm>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 26aa7ee..5395229 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -90,6 +90,7 @@
<!ENTITY brin SYSTEM "brin.sgml">
<!ENTITY planstats SYSTEM "planstats.sgml">
<!ENTITY indexam SYSTEM "indexam.sgml">
+<!ENTITY seqam SYSTEM "seqam.sgml">
<!ENTITY nls SYSTEM "nls.sgml">
<!ENTITY plhandler SYSTEM "plhandler.sgml">
<!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index e378d69..a3e8156 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -249,6 +249,7 @@
&spgist;
&gin;
&brin;
+ &seqam;
&storage;
&bki;
&planstats;
diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 47d3c82..ba2c3d9 100644
--- a/doc/src/sgml/ref/alter_sequence.sgml
+++ b/doc/src/sgml/ref/alter_sequence.sgml
@@ -29,9 +29,15 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [
[ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
[ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
[ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+ [ USING <replaceable class="parameter">access_method</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ] ]
ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+ SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+ RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
</synopsis>
</refsynopsisdiv>
@@ -221,6 +227,24 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
</varlistentry>
<varlistentry>
+ <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+ <listitem>
+ <para>
+ The <literal>USING</literal> option specifies which sequence access
+ method will be used when generating the sequence numbers.
+ </para>
+ <para>
+ When the <literal>RESTART WITH <replaceable
+ class="parameter">restart</replaceable></literal> parameter is also
+ given, it will be used as a starting value for the new access method.
+ Otherwise the <function>nextval</> function will be called with the old
+ access method and the result will be used as start value for the new
+ access method.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><replaceable class="PARAMETER">new_owner</replaceable></term>
<listitem>
<para>
@@ -247,6 +271,37 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
+ <listitem>
+ <para>
+ This clause changes one or more storage parameters for the sequence
+ access method.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )</literal></term>
+ <listitem>
+ <para>
+ This clause resets one or more storage parameters to their defaults.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+ <listitem>
+ <para>
+ This clause specifies optional storage parameters for the new sequence
+ access method.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index 9e364ff..85840ff 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -25,6 +25,8 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="param
[ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
[ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
[ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+ [ USING <replaceable class="parameter">access_method</replaceable> ]
+ [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
</synopsis>
</refsynopsisdiv>
@@ -223,6 +225,29 @@ SELECT * FROM <replaceable>name</replaceable>;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>USING</literal> <replaceable class="parameter">access_method</replaceable></term>
+ <listitem>
+ <para>
+ The <literal>USING</literal> option specifies which sequence access
+ method will be used when generating the sequence numbers. The default
+ is <literal>"local"</>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+ <listitem>
+ <para>
+ This clause specifies optional storage parameters for a sequence access
+ method.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/doc/src/sgml/seqam.sgml b/doc/src/sgml/seqam.sgml
new file mode 100644
index 0000000..a0aaa4b
--- /dev/null
+++ b/doc/src/sgml/seqam.sgml
@@ -0,0 +1,289 @@
+<!-- doc/src/sgml/seqam.sgml -->
+
+<chapter id="seqam">
+ <title>Sequence Access Method Interface Definition</title>
+
+ <para>
+ This chapter describes the interface for writing sequence access methods.
+ </para>
+
+ <para>
+ The sequence access method defines behavior of a sequence which is using it.
+ Mainly how the new values are generated. Each sequence can have different
+ access method in order to behave in a way that makes most sense in the
+ application context.
+ </para>
+
+ <sect1 id="seqam-catalog">
+ <title>Catalog Entries for Sequence Access Method</title>
+
+ <para>
+ Each sequence access method is described by a row in the
+ <structname>pg_seqam</structname> system catalog (see
+ <xref linkend="catalog-pg-seqam">). The contents of a
+ <structname>pg_seqam</structname> row is the name of the access method
+ and are references to
+ <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>
+ entries that identify the functions provided by the access method.
+ functions supplied by the access method.
+ </para>
+ </sect1>
+
+ <sect1 id="seqam-functions">
+ <title>Sequence Access Method Functions</title>
+
+ <para>
+ The behaviour of a sequence access method is defined by a set of functions.
+ These functions are implemented by the sequence access method.
+ </para>
+
+ <para>
+ The functions defining a sequence access method are:
+ </para>
+
+ <para>
+<programlisting>
+HeapTuple
+seqam_init (Relation seqrel, HeapTuple tuple, int64 restart_value,
+ bool restart_requested, bool is_init);
+</programlisting>
+ Initialize the sequence's internal state. This function is called both
+ when a new sequence is created, when the <command>ALTER SEQUENCE</> command
+ is called for an existing sequence or by the
+ <command>TRUNCATE RESTART IDENTITY</> command.
+ </para>
+
+ <para>
+ The <literal>seqrel</> points to relcache record representing the
+ <link linkend="catalog-pg-class"><structname>pg_class</structname></link>
+ tuple tuple of the sequence.
+ <literal>tuple</> is in-memory version of the
+ <structname>pg_sequence</structname> tuple itself. The
+ <literal>restart_value</> is value the sequence should start from and the
+ <literal>restart_requested</> gives indication if the
+ <literal>restart_value</> should indeed be used. Finally <literal>is_init</>
+ is <literal>true</> when this is new sequence being created or when the
+ access method of a sequence has been changed, otherwise it's
+ <literal>false</>.
+ to the options in the <command>CREATE SEQUENCE</> or
+ <command>ALTER SEQUENCE</> statement.
+ <literal>reloptions</> contains parsed reloptions passed to the
+ <command>CREATE SEQUENCE</> or <command>ALTER SEQUENCE</> statements.
+ The <literal>values</> and <literal>nulls</> describe new tuple for the
+ <structname>pg_sequence</structname> tuple which this function can update
+ as needed.
+ </para>
+
+ <para>
+ This function should return the updated
+ <structname>pg_sequence</structname>. If the tuple does not have to be
+ updated returning the input <literal>tuple</> is acceptable.
+ </para>
+
+ <para>
+<programlisting>
+bytea *
+seqam_reloptions (ArrayType *reloptions, bool validate);
+</programlisting>
+ Parse and validate the reloptions array for an sequence.
+ <parameter>reloptions</> is a <type>text</> array containing entries of the
+ form <replaceable>name</><literal>=</><replaceable>value</>.
+ The function should construct a <type>bytea</> value, which will be then sent
+ to the <function>seqam_init</> and stored in the catalog.
+ When <parameter>validate</> is true, the function should report a suitable
+ error message if any of the options are unrecognized or have invalid
+ values; when <parameter>validate</> is false, invalid entries should be
+ silently ignored. (<parameter>validate</> is false when loading options
+ already stored in <structname>pg_catalog</>; an invalid entry could only
+ be found if the access method has changed its rules for options, and in
+ that case ignoring obsolete entries is appropriate.)
+ It is OK to return NULL if default behavior is wanted.
+ </para>
+
+ <para>
+<programlisting>
+int64
+seqam_alloc (Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+ int64 *last);
+</programlisting>
+ Allocate new sequence value(s). The <literal>nrequested</> specifies how
+ many new values should be allocated by this call. Return value is the next
+ allocated value and <literal>last</> should be set to last allocated value.
+ </para>
+
+ <para>
+<programlisting>
+bool
+seqam_setval (Relation seqrel, SequenceHandle *seqh, int64 new_value)
+</programlisting>
+ Set the current sequence value the the <literal>new_value</>.
+ </para>
+
+ <para>
+<programlisting>
+ArrayType *
+seqam_get_state (Relation seqrel, SequenceHandle *seqh);
+</programlisting>
+ Dump the current state of the sequence. The return value is one dimensional
+ <literal>TEXT</> array containing list of key/value pairs it the form
+ <literal>{'key1', 'value1', 'key2', 'value2'}</>. This interface is mainly
+ used by <command>pg_dump</command>.
+ </para>
+
+ <para>
+<programlisting>
+void
+seqam_set_state (Relation seqrel, SequenceHandle *seqh, ArrayType *statearr);
+</programlisting>
+ Restore state of the sequence based on the key/value pairs defined in
+ the <literal>statearr</> in <literal>{'key1', 'value1', 'key2', 'value2'}</>
+ form. The <literal>statearr</> is defined as <literal>TEXT[]</> in SQL. This
+ function must accept output of the <function>seqam_get_state()</> function.
+ This interface is mainly used by <command>pg_dump</command>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="seqam-storage">
+ <title>Sequence Access Method Storage API</title>
+
+ <para>
+ To store the current state of the sequence, the backend provides the
+ following functions which the sequence access method can use:
+ </para>
+
+ <para>
+<programlisting>
+void
+sequence_open(Oid relid, SequenceHandle *seqh);
+</programlisting>
+ Open sequence with given <literal>relid</>. The <literal>seqh</> is
+ output parameter which will be set to the sequence handle.
+ </para>
+
+ <para>
+<programlisting>
+void
+sequence_close (SequenceHandle *seqh);
+</programlisting>
+ Release and close the opened sequence.
+ </para>
+
+ <para>
+<programlisting>
+HeapTuple
+sequence_read_tuple (SequenceHandle *seqh);
+</programlisting>
+ Reads and locks the sequence tuple for processing by the access method.
+ The tuple should be released by calling <function>sequence_release_tuple</>.
+ </para>
+
+<programlisting>
+HeapTuple
+sequence_swap_tuple (SequenceHandle *seqh, HeapTuple *newtup);
+</programlisting>
+ Changes the working tuple to the <literal>newtup</>. This does not change
+ the sequence itself, only the state of the <struct>SequenceHandle</>. To
+ save the tuple the <function>sequence_*_update()</> sequence has to be
+ called. The tuple should be released by calling
+ <function>sequence_release_tuple</>. Note that you can't call this function
+ without calling <function>sequence_read_tuple()</> first.
+ </para>
+
+ <para>
+<programlisting>
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal);
+</programlisting>
+ Start the sequence update. The <literal>do_wal</> gives hint if at least
+ one of subsequent <function>sequence_apply_update()</> calls will be with
+ <literal>do_wal = true</>.
+</para>
+
+ <para>
+<programlisting>
+void
+sequence_apply_update(SequenceHandle *seqh, bool do_wal);
+</programlisting>
+ Save the modified sequence tuple indicating if the change should be WAL
+ logged as well as saved to the catalog.
+ </para>
+
+ <para>
+<programlisting>
+void
+sequence_finish_update(SequenceHandle *seqh, HeapTuple newtuple);
+</programlisting>
+ Finish the sequence update.
+ </para>
+
+ <para>
+ To update the sequence tuple you have to call all three above functions in
+ order to be sure the update is applied in error safe manner. The
+ <function>sequence_apply_update()</> can be called multiple times between
+ single pair of <function>sequence_start_update()</> and
+ <function>sequence_finish_update()</> calls. Update function cannot be called
+ if the tuple was already released by the
+ <function>sequence_release_tuple()</> function.
+ </para>
+
+ <para>
+<programlisting>
+void
+sequence_release_tuple(SequenceHandle *seqh);
+</programlisting>
+ Release the tuple read and locked by <function>sequence_read_tuple</> and/or
+ added by <function>sequence_swap_tuple</>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="seqam-utility">
+ <title>Sequence Access Method Utility Functions</title>
+
+ <para>
+ Additional utility functions which can be useful when writing sequence
+ access methods:
+ </para>
+
+ <para>
+<programlisting>
+bool
+sequence_needs_wal(SequenceHandle *seqh);
+</programlisting>
+ Returns <literal>true</> if the sequence tuple was last saved before last
+ checkpoint. This can be help when deciding what to set <literal>do_wal</>
+ to when calling <function>sequence_apply_update</>.
+ </para>
+
+ <para>
+<programlisting>
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+ int64 minv, int64 maxv, int64 incby,
+ bool is_cycled, bool report_errors);
+</programlisting>
+ Helper function to increment value of a sequence, correctly handling
+ sequence options like min value, max value, increment value and cycling of
+ the sequence value. The <literal>value</> is pointer to current value which
+ should be incremented, <literal>incnum</> specifies how much should the
+ value be incremented, the rest of the options should be set to values
+ specified by the sequence's <structname>pg_sequence</structname> tuple.
+ Returns by how much was the value increased.
+ </para>
+
+ <para>
+<programlisting>
+void
+sequence_check_range (int64 value, int64 min_value,
+ int64 max_value, const char *valname);
+</programlisting>
+ Checks if the <literal>value</> is between <literal>min_value</>
+ and <literal>max_value</> and raises standard error message if it's not.
+ The <literal>valname</> is used as the name of the wrong value in the error
+ message.
+ </para>
+
+ </sect1>
+
+</chapter>
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..818da15 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,7 @@ subdir = src/backend/access
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-SUBDIRS = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+ transam sequence
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 8176b6a..9543503 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -829,7 +829,8 @@ untransformRelOptions(Datum options)
* instead.
*
* tupdesc is pg_class' tuple descriptor. amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * in the case of the tuple corresponding to an index or sequence, InvalidOid
+ * otherwise.
*/
bytea *
extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
@@ -860,7 +861,10 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
options = view_reloptions(datum, false);
break;
case RELKIND_INDEX:
- options = index_reloptions(amoptions, datum, false);
+ options = am_reloptions(amoptions, datum, false);
+ break;
+ case RELKIND_SEQUENCE:
+ options = am_reloptions(amoptions, datum, false);
break;
case RELKIND_FOREIGN_TABLE:
options = NULL;
@@ -1308,14 +1312,14 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
/*
- * Parse options for indexes.
+ * Parse options for indexes or sequences.
*
* amoptions Oid of option parser
* reloptions options as text[] datum
* validate error flag
*/
bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
{
FmgrInfo flinfo;
FunctionCallInfoData fcinfo;
diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile
new file mode 100644
index 0000000..01a0dc8
--- /dev/null
+++ b/src/backend/access/sequence/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+# Makefile for access/sequence
+#
+# IDENTIFICATION
+# src/backend/access/sequence/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/sequence
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = seqam.o seqlocal.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
new file mode 100644
index 0000000..161ec21
--- /dev/null
+++ b/src/backend/access/sequence/seqam.c
@@ -0,0 +1,308 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.c
+ * general sequence access method routines
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/sequence/seqam.c
+ *
+ *
+ * Sequence access method allows the SQL Standard Sequence objects to be
+ * managed according to either the default access method or a pluggable
+ * replacement. Each sequence can only use one access method at a time,
+ * though different sequence access methods can be in use by different
+ * sequences at the same time.
+ *
+ * The SQL Standard assumes that each Sequence object is completely controlled
+ * from the current database node, preventing any form of clustering mechanisms
+ * from controlling behaviour. Sequence access methods are general purpose
+ * though designed specifically to address the needs of Sequences working as
+ * part of a multi-node "cluster", though that is not defined here, nor are
+ * there dependencies on anything outside of this module, nor any particular
+ * form of clustering.
+ *
+ * The SQL Standard behaviour, also the historical PostgreSQL behaviour, is
+ * referred to as the "Local" SeqAM. That is also the basic default. Local
+ * SeqAM assumes that allocations from the sequence will be contiguous, so if
+ * user1 requests a range of values and is given 500-599 as values for their
+ * backend then the next user to make a request will be given a range starting
+ * with 600.
+ *
+ * The SeqAM mechanism allows us to override the Local behaviour, for use with
+ * clustering systems. When multiple masters can request ranges of values it
+ * would break the assumption of contiguous allocation. It seems likely that
+ * the SeqAM would also wish to control node-level caches for sequences to
+ * ensure good performance. The CACHE option and other options may be
+ * overridden by the _init API call, if needed, though in general having
+ * cacheing per backend and per node seems desirable.
+ *
+ * SeqAM allows calls to allocate a new range of values, reset the sequence to
+ * a new value and to define options for the AM module. The on-disk format of
+ * Sequences is the same for all AMs, except that each sequence has a SeqAm
+ * defined private-data column, amdata.
+ *
+ * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores the
+ * Oid of the SeqAM, just as we do for IndexAm. The relcache stores AM
+ * information in much the same way for indexes and sequences, and management
+ * of options is similar also.
+ *
+ * Note that the SeqAM API calls are synchronous. It is up to the SeqAM to
+ * decide how that is handled, for example, whether there is a higher level
+ * cache at instance level to amortise network traffic in cluster.
+ *
+ * The SeqAM is identified by Oid of corresponding tuple in pg_seqam. There is
+ * no syscache for pg_seqam, though the SeqAM data is stored on the relcache
+ * entry for the sequence.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "access/relscan.h"
+#include "access/seqam.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_seqam.h"
+#include "utils/guc.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+char *serial_seqam = NULL;
+
+#define GET_SEQAM_PROCEDURE(pname, missing_ok) \
+do { \
+ procedure = &seqrel->rd_seqaminfo->pname; \
+ if (!OidIsValid(procedure->fn_oid)) \
+ { \
+ RegProcedure procOid = seqrel->rd_seqam->pname; \
+ if (RegProcedureIsValid(procOid)) \
+ fmgr_info_cxt(procOid, procedure, seqrel->rd_indexcxt); \
+ else if (!missing_ok) \
+ elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
+ } \
+} while(0)
+
+/*-------------------------------------------------------------------------
+ *
+ * Sequence Access Method API
+ *
+ * INTERFACE ROUTINES
+ * seqam_init - initialize sequence, also used for resetting
+ * seqam_alloc - allocate a new range of values for the sequence
+ * seqam_setval - implements the setval SQL interface
+ * seqam_get_state - dump sequence state (for pg_dump)
+ * seqam_set_state - restore sequence state (for pg_dump)
+ *
+ * Additionally, the am_reloptions interface is used for parsing reloptions
+ * that can be used for passing AM specific options.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * seqam_init - initialize/replace custom sequence am values
+ */
+HeapTuple
+seqam_init(Relation seqrel, HeapTuple tuple, int64 restart_value,
+ bool restart_requested, bool is_init)
+{
+ FmgrInfo procedure;
+ Datum ret;
+
+ fmgr_info(seqrel->rd_seqam->seqaminit, &procedure);
+
+ /*
+ * Have the seqam's proc do its work.
+ */
+ ret = FunctionCall5(&procedure,
+ PointerGetDatum(seqrel),
+ PointerGetDatum(tuple),
+ Int64GetDatum(restart_value),
+ BoolGetDatum(restart_requested),
+ BoolGetDatum(is_init));
+
+ return (HeapTuple) DatumGetPointer(ret);
+}
+
+/*
+ * seqam_alloc - allocate sequence values in a sequence
+ */
+int64
+seqam_alloc(Relation seqrel, SequenceHandle *seqh, int64 nrequested,
+ int64 *last)
+{
+ FmgrInfo *procedure;
+ Datum ret;
+
+ Assert(RelationIsValid(seqrel));
+ Assert(PointerIsValid(seqrel->rd_seqam));
+ Assert(OidIsValid(seqrel->rd_rel->relam));
+
+ GET_SEQAM_PROCEDURE(seqamalloc, false);
+
+ /*
+ * have the seqam's alloc proc do its work.
+ */
+ ret = FunctionCall4(procedure,
+ PointerGetDatum(seqrel),
+ PointerGetDatum(seqh),
+ Int64GetDatum(nrequested),
+ PointerGetDatum(last));
+ return DatumGetInt64(ret);
+}
+
+/*
+ * seqam_setval - set sequence values in a sequence
+ */
+void
+seqam_setval(Relation seqrel, SequenceHandle *seqh, int64 new_value)
+{
+ FmgrInfo *procedure;
+
+ Assert(RelationIsValid(seqrel));
+ Assert(PointerIsValid(seqrel->rd_seqam));
+ Assert(OidIsValid(seqrel->rd_rel->relam));
+
+ GET_SEQAM_PROCEDURE(seqamsetval, true);
+
+ if (!OidIsValid(procedure->fn_oid))
+ return;
+
+ /*
+ * have the seqam's setval proc do its work.
+ */
+ FunctionCall3(procedure,
+ PointerGetDatum(seqrel),
+ PointerGetDatum(seqh),
+ Int64GetDatum(new_value));
+}
+
+/*
+ * seqam_get_state - pg_dump support
+ */
+ArrayType *
+seqam_get_state(Relation seqrel, SequenceHandle *seqh)
+{
+ FmgrInfo procedure;
+ Datum statearr;
+
+ Assert(RelationIsValid(seqrel));
+ Assert(PointerIsValid(seqrel->rd_seqam));
+ Assert(OidIsValid(seqrel->rd_rel->relam));
+
+ fmgr_info(seqrel->rd_seqam->seqamgetstate, &procedure);
+
+ /*
+ * have the seqam's setval proc do its work.
+ */
+ statearr = FunctionCall2(&procedure,
+ PointerGetDatum(seqrel),
+ PointerGetDatum(seqh));
+
+ return (ArrayType *) DatumGetPointer(statearr);
+}
+
+/*
+ * seqam_set_state - restore from pg_dump
+ */
+void
+seqam_set_state(Relation seqrel, SequenceHandle *seqh, ArrayType *statearr)
+{
+ FmgrInfo procedure;
+
+ Assert(RelationIsValid(seqrel));
+ Assert(PointerIsValid(seqrel->rd_seqam));
+ Assert(OidIsValid(seqrel->rd_rel->relam));
+
+ fmgr_info(seqrel->rd_seqam->seqamsetstate, &procedure);
+
+ /*
+ * have the seqam's setval proc do its work.
+ */
+ FunctionCall3(&procedure,
+ PointerGetDatum(seqrel),
+ PointerGetDatum(seqh),
+ PointerGetDatum(statearr));
+}
+
+
+/*------------------------------------------------------------
+ *
+ * Sequence Access Manager management functions
+ *
+ *------------------------------------------------------------
+ */
+
+/* check_hook: validate new serial_seqam value */
+bool
+check_serial_seqam(char **newval, void **extra, GucSource source)
+{
+ /*
+ * If we aren't inside a transaction, we cannot do database access so
+ * cannot verify the name. Must accept the value on faith.
+ */
+ if (IsTransactionState())
+ {
+ if (!OidIsValid(get_seqam_oid(*newval, true)))
+ {
+ /*
+ * When source == PGC_S_TEST, we are checking the argument of an
+ * ALTER DATABASE SET or ALTER USER SET command. Value may
+ * be created later. Because of that, issue a NOTICE if source ==
+ * PGC_S_TEST, but accept the value anyway.
+ */
+ if (source == PGC_S_TEST)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("sequence access method \"%s\" does not exist",
+ *newval)));
+ }
+ else
+ {
+ GUC_check_errdetail("sequence access method \"%s\" does not exist.",
+ *newval);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+/*
+ * get_seqam_oid - given a sequence AM name, look up the OID
+ *
+ * If missing_ok is false, throw an error if SeqAM name not found. If true,
+ * just return InvalidOid.
+ */
+Oid
+get_seqam_oid(const char *amname, bool missing_ok)
+{
+ Oid result;
+ HeapTuple tuple;
+
+ /* look up the access method */
+ tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname));
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ {
+ result = HeapTupleGetOid(tuple);
+ ReleaseSysCache(tuple);
+ }
+ else
+ result = InvalidOid;
+
+ if (!OidIsValid(result) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("sequence access method \"%s\" does not exist",
+ amname)));
+ return result;
+}
diff --git a/src/backend/access/sequence/seqlocal.c b/src/backend/access/sequence/seqlocal.c
new file mode 100644
index 0000000..a891bd8
--- /dev/null
+++ b/src/backend/access/sequence/seqlocal.c
@@ -0,0 +1,383 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqlocal.c
+ * Local sequence access manager
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/sequence/seqlocal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/int8.h"
+#include "utils/rel.h"
+
+/*
+ * We don't want to log each fetching of a value from a sequence,
+ * so we pre-log a few fetches in advance. In the event of
+ * crash we can lose (skip over) as many values as we pre-logged.
+ */
+#define SEQ_LOG_VALS 32
+
+/* Definition of additional columns for local sequence. */
+typedef struct LocalAmdata
+{
+ int64 log_cnt;
+} LocalAmdata;
+
+
+/*
+ * seqam_local_reloptions()
+ *
+ * Parse and verify the reloptions of a local sequence.
+ */
+Datum
+seqam_local_reloptions(PG_FUNCTION_ARGS)
+{
+ Datum reloptions = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+
+ if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("local sequence does not accept any storage parameters")));
+
+ PG_RETURN_NULL();
+}
+
+/*
+ * seqam_local_init()
+ *
+ * Initialize local sequence
+ */
+Datum
+seqam_local_init(PG_FUNCTION_ARGS)
+{
+ Relation seqrel = (Relation) PG_GETARG_POINTER(0);
+ HeapTuple tuple = (HeapTuple) PG_GETARG_POINTER(1);
+ int64 restart_value = PG_GETARG_INT64(2);
+ bool restart_requested = PG_GETARG_BOOL(3);
+ bool is_init = PG_GETARG_BOOL(4);
+ bool isnull;
+ Datum amdata;
+ TupleDesc tupDesc = RelationGetDescr(seqrel);
+ Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(tuple);
+ LocalAmdata *localseq;
+
+ /*
+ * If this is a new sequence or RESTART was provided in ALTER we should
+ * reset our state to that new starting point.
+ */
+ if (is_init || restart_requested)
+ {
+ seq->last_value = restart_value;
+ seq->is_called = false;
+ }
+
+ amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+ &isnull);
+
+ /* Make sure amdata is filled. */
+ if (isnull)
+ {
+ Datum values[SEQ_COL_LASTCOL];
+ bool nulls[SEQ_COL_LASTCOL];
+ bool replace[SEQ_COL_LASTCOL];
+ struct varlena *vl = palloc0(VARHDRSZ + sizeof(LocalAmdata));
+ HeapTuple newtup;
+
+ memset(replace, false, sizeof(replace));
+ memset(nulls, false, sizeof(nulls));
+ memset(values, 0, sizeof(values));
+
+ replace[SEQ_COL_AMDATA - 1] = true;
+ nulls[SEQ_COL_AMDATA - 1] = false;
+
+ SET_VARSIZE(vl, VARHDRSZ + sizeof(LocalAmdata));
+ values[SEQ_COL_AMDATA - 1] = PointerGetDatum(vl);
+
+ newtup = heap_modify_tuple(tuple, tupDesc, values, nulls, replace);
+
+ /* Don't leak memory. */
+ heap_freetuple(tuple);
+ tuple = newtup;
+
+ amdata = fastgetattr(tuple, SEQ_COL_AMDATA, tupDesc,
+ &isnull);
+ }
+
+ localseq = (LocalAmdata *)
+ VARDATA_ANY(DatumGetByteaP(amdata));
+
+ /* We always reset the log_cnt. */
+ localseq->log_cnt = 0;
+
+ PG_RETURN_POINTER(tuple);
+}
+
+/*
+ * seqam_local_alloc()
+ *
+ * Allocate a new range of values for a local sequence.
+ */
+Datum
+seqam_local_alloc(PG_FUNCTION_ARGS)
+{
+ Relation seqrel = (Relation) PG_GETARG_POINTER(0);
+ SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+ int64 nrequested = PG_GETARG_INT64(2);
+ int64 *last = (int64 *) PG_GETARG_POINTER(3);
+ FormData_pg_sequence *seq;
+ int64 incby,
+ maxv,
+ minv,
+ log,
+ fetch,
+ result,
+ next,
+ rescnt = 0;
+ bool is_cycled,
+ is_called,
+ logit = false;
+ LocalAmdata *localseq;
+
+ seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+ localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+ next = result = seq->last_value;
+ incby = seq->increment_by;
+ maxv = seq->max_value;
+ minv = seq->min_value;
+ is_cycled = seq->is_cycled;
+ fetch = nrequested;
+ log = localseq->log_cnt;
+ is_called = seq->is_called;
+
+ /* We are returning last_value if not is_called so fetch one less value. */
+ if (!is_called)
+ {
+ nrequested--;
+ fetch--;
+ }
+
+ /*
+ * Decide whether we should emit a WAL log record. If so, force up the
+ * fetch count to grab SEQ_LOG_VALS more values than we actually need to
+ * cache. (These will then be usable without logging.)
+ *
+ * If this is the first nextval after a checkpoint, we must force a new
+ * WAL record to be written anyway, else replay starting from the
+ * checkpoint would fail to advance the sequence past the logged values.
+ * In this case we may as well fetch extra values.
+ */
+ if (log < fetch || !is_called)
+ {
+ /* Forced log to satisfy local demand for values. */
+ fetch = log = fetch + SEQ_LOG_VALS;
+ logit = true;
+ }
+ else if (sequence_needs_wal(seqh))
+ {
+ fetch = log = fetch + SEQ_LOG_VALS;
+ logit = true;
+ }
+
+ /* Fetch new result value if is_called. */
+ if (is_called)
+ {
+ rescnt += sequence_increment(seqrel, &next, 1, minv, maxv, incby,
+ is_cycled, true);
+ result = next;
+ }
+
+ /* Fetch as many values as was requested by backend. */
+ if (rescnt < nrequested)
+ rescnt += sequence_increment(seqrel, &next, nrequested - rescnt, minv,
+ maxv, incby, is_cycled, false);
+
+ /* Last value available for calling backend. */
+ *last = next;
+ /* Values we made available to calling backend can't be counted as cached. */
+ log -= rescnt;
+
+ /* We might need to fetch even more values for our own caching. */
+ if (rescnt < fetch)
+ rescnt += sequence_increment(seqrel, &next, fetch - rescnt, minv,
+ maxv, incby, is_cycled, false);
+
+ fetch -= rescnt;
+ log -= fetch; /* adjust for any unfetched numbers */
+ Assert(log >= 0);
+
+ /*
+ * Log our cached data.
+ */
+ sequence_start_update(seqh, logit);
+ if (logit)
+ {
+ seq->last_value = next;
+ seq->is_called = true;
+ localseq->log_cnt = 0;
+
+ sequence_apply_update(seqh, true);
+ }
+
+ /* Now update sequence tuple to the intended final state */
+ seq->last_value = *last; /* last fetched number */
+ seq->is_called = true;
+ localseq->log_cnt = log; /* how much is logged */
+
+ sequence_apply_update(seqh, false);
+ sequence_finish_update(seqh);
+
+ PG_RETURN_INT64(result);
+}
+
+/*
+ * seqam_local_setval()
+ *
+ * Set value of a local sequence
+ */
+Datum
+seqam_local_setval(PG_FUNCTION_ARGS)
+{
+ SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+ int64 next = PG_GETARG_INT64(2);
+ FormData_pg_sequence *seq;
+ LocalAmdata *localseq;
+
+ seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+ localseq = (LocalAmdata *) VARDATA_ANY(&seq->amdata);
+
+ seq->last_value = next; /* last fetched number */
+ seq->is_called = true;
+ localseq->log_cnt = 0; /* how much is logged */
+
+ sequence_start_update(seqh, true);
+ sequence_apply_update(seqh, true);
+ sequence_finish_update(seqh);
+ sequence_release_tuple(seqh);
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * seqam_local_get_state()
+ *
+ * Dump state of a local sequence (for pg_dump)
+ */
+Datum
+seqam_local_get_state(PG_FUNCTION_ARGS)
+{
+ SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+ Datum datums[4];
+ Datum val;
+ FormData_pg_sequence *seq;
+
+ seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+
+ datums[0] = CStringGetTextDatum("last_value");
+ val = DirectFunctionCall1(int8out, Int64GetDatum(seq->last_value));
+ datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+ datums[2] = CStringGetTextDatum("is_called");
+ val = DirectFunctionCall1(boolout, BoolGetDatum(seq->is_called));
+ datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+ sequence_release_tuple(seqh);
+
+ PG_RETURN_ARRAYTYPE_P(construct_array(datums, 4, TEXTOID, -1, false, 'i'));
+}
+
+/*
+ * seqam_local_set_state()
+ *
+ * Restore previously dumped state of local sequence (used by pg_dump)
+*/
+Datum
+seqam_local_set_state(PG_FUNCTION_ARGS)
+{
+ SequenceHandle *seqh = (SequenceHandle *) PG_GETARG_POINTER(1);
+ ArrayType *statearr = PG_GETARG_ARRAYTYPE_P(2);
+ FormData_pg_sequence *seq;
+ int64 last_value = 0;
+ bool is_called = false;
+ bool last_value_found = false,
+ is_called_found = false;
+ Datum *datums;
+ int count;
+ int i;
+
+ Assert(ARR_ELEMTYPE(statearr) == TEXTOID && ARR_NDIM(statearr) == 1 &&
+ (ARR_DIMS(statearr)[0]) % 2 == 0 && !ARR_HASNULL(statearr));
+
+ deconstruct_array(statearr,
+ TEXTOID, -1, false, 'i',
+ &datums, NULL, &count);
+ count /= 2;
+ for (i = 0; i < count; ++i)
+ {
+ char *key,
+ *val;
+
+ key = TextDatumGetCString(datums[i * 2]);
+ val = TextDatumGetCString(datums[i * 2 + 1]);
+
+ if (pg_strcasecmp(key, "last_value") == 0)
+ {
+ last_value = DatumGetInt64(DirectFunctionCall1(int8in,
+ CStringGetDatum(val)));
+ last_value_found = true;
+ }
+ else if (pg_strcasecmp(key, "is_called") == 0)
+ {
+ is_called = DatumGetBool(DirectFunctionCall1(boolin,
+ CStringGetDatum(val)));
+ is_called_found = true;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid state key \"%s\" for local sequence",
+ key)));
+ }
+
+ if (!last_value_found)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("last_value is required parameter for local sequence")));
+
+ if (!is_called_found)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("is_called is required parameter for local sequence")));
+
+
+ seq = (FormData_pg_sequence *) GETSTRUCT(sequence_read_tuple(seqh));
+ sequence_check_range(last_value, seq->min_value, seq->max_value, "last_value");
+
+ sequence_start_update(seqh, true);
+ seq->last_value = last_value;
+ seq->is_called = is_called;
+ sequence_apply_update(seqh, true);
+ sequence_finish_update(seqh);
+ sequence_release_tuple(seqh);
+
+ PG_FREE_IF_COPY(statearr, 2);
+
+ PG_RETURN_VOID();
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 6e563b6..1c85569 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -252,6 +252,7 @@ Boot_CreateStmt:
false,
true,
false,
+ InvalidOid,
NULL);
elog(DEBUG4, "relation created with OID %u", id);
}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index c73f20d..4fd8588 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_statistic.h pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \
pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
- pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+ pg_authid.h pg_auth_members.h pg_seqam.h pg_shdepend.h pg_shdescription.h \
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h pg_extension.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d04e94d..1fd46a6 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -87,6 +87,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
Oid reloftype,
Oid relowner,
char relkind,
+ Oid relam,
Datum relacl,
Datum reloptions);
static ObjectAddress AddNewRelationType(const char *typeName,
@@ -847,6 +848,7 @@ AddNewRelationTuple(Relation pg_class_desc,
Oid reloftype,
Oid relowner,
char relkind,
+ Oid relam,
Datum relacl,
Datum reloptions)
{
@@ -920,6 +922,7 @@ AddNewRelationTuple(Relation pg_class_desc,
new_rel_reltup->relowner = relowner;
new_rel_reltup->reltype = new_type_oid;
new_rel_reltup->reloftype = reloftype;
+ new_rel_reltup->relam = relam;
new_rel_desc->rd_att->tdtypeid = new_type_oid;
@@ -1033,6 +1036,7 @@ heap_create_with_catalog(const char *relname,
bool use_user_acl,
bool allow_system_table_mods,
bool is_internal,
+ Oid relam,
ObjectAddress *typaddress)
{
Relation pg_class_desc;
@@ -1261,6 +1265,7 @@ heap_create_with_catalog(const char *relname,
reloftypeid,
ownerid,
relkind,
+ relam,
PointerGetDatum(relacl),
reloptions);
@@ -1273,7 +1278,8 @@ heap_create_with_catalog(const char *relname,
/*
* Make a dependency link to force the relation to be deleted if its
* namespace is. Also make a dependency link to its owner, as well as
- * dependencies for any roles mentioned in the default ACL.
+ * dependencies for any roles mentioned in the default ACL. When relam
+ * is specified, record dependency on the
*
* For composite types, these dependencies are tracked for the pg_type
* entry, so we needn't record them here. Likewise, TOAST tables don't
@@ -1328,6 +1334,14 @@ heap_create_with_catalog(const char *relname,
0, NULL,
nnewmembers, newmembers);
}
+
+ if (relkind == RELKIND_SEQUENCE)
+ {
+ referenced.classId = SeqAccessMethodRelationId;
+ referenced.objectId = relam;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/* Post creation hook for new relation */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 10f0396..52d1d59 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/seqam.h"
#include "access/sysattr.h"
#include "catalog/catalog.h"
#include "catalog/indexing.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index c99d353..0b35046 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -290,6 +290,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
false,
true,
true,
+ InvalidOid,
NULL);
Assert(toast_relid != InvalidOid);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 3febdd5..e2c1990 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -678,6 +678,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
false,
true,
true,
+ InvalidOid,
NULL);
Assert(OIDNewHeap != InvalidOid);
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 54b2f38..7d4133b 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -385,7 +385,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/*
* Actually create the target table
*/
- intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL);
+ intoRelationAddr = DefineRelation(create, relkind, InvalidOid, InvalidOid,
+ NULL);
/*
* If necessary, create a TOAST table for the target table. Note that
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 351d48e..bcf1065 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -527,7 +527,7 @@ DefineIndex(Oid relationId,
reloptions = transformRelOptions((Datum) 0, stmt->options,
NULL, NULL, false, false);
- (void) index_reloptions(amoptions, reloptions, true);
+ (void) am_reloptions(amoptions, reloptions, true);
/*
* Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 6d316d6..060b3c4 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -14,6 +14,9 @@
*/
#include "postgres.h"
+#include "access/reloptions.h"
+#include "access/seqam.h"
+#include "access/transam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/transam.h"
@@ -22,8 +25,10 @@
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "catalog/dependency.h"
+#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
+#include "catalog/pg_seqam.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
@@ -36,19 +41,14 @@
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/int8.h"
#include "utils/lsyscache.h"
+#include "utils/rel.h"
#include "utils/resowner.h"
#include "utils/syscache.h"
/*
- * We don't want to log each fetching of a value from a sequence,
- * so we pre-log a few fetches in advance. In the event of
- * crash we can lose (skip over) as many values as we pre-logged.
- */
-#define SEQ_LOG_VALS 32
-
-/*
* The "special area" of a sequence's buffer page looks like this.
*/
#define SEQ_MAGIC 0x1717
@@ -81,6 +81,16 @@ typedef SeqTableData *SeqTable;
static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
+struct SequenceHandle
+{
+ SeqTable elm;
+ Relation rel;
+ Buffer buf;
+ HeapTupleData tup;
+ Page temppage;
+ bool inupdate;
+};
+
/*
* last_used_seq is updated by nextval() to point to the last used
* sequence.
@@ -91,13 +101,11 @@ static void fill_seq_with_data(Relation rel, HeapTuple tuple);
static int64 nextval_internal(Oid relid);
static Relation open_share_lock(SeqTable seq);
static void create_seq_hashtable(void);
-static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
- Buffer *buf, HeapTuple seqtuple);
static void init_params(List *options, bool isInit,
Form_pg_sequence new, List **owned_by);
-static void do_setval(Oid relid, int64 next, bool iscalled);
static void process_owned_by(Relation seqrel, List *owned_by);
+static void log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+ Buffer buf, Page page);
/*
@@ -111,6 +119,7 @@ DefineSequence(CreateSeqStmt *seq)
List *owned_by;
CreateStmt *stmt = makeNode(CreateStmt);
Oid seqoid;
+ Oid seqamid;
ObjectAddress address;
Relation rel;
HeapTuple tuple;
@@ -147,6 +156,11 @@ DefineSequence(CreateSeqStmt *seq)
/* Check and set all option values */
init_params(seq->options, true, &new, &owned_by);
+ if (seq->accessMethod)
+ seqamid = get_seqam_oid(seq->accessMethod, false);
+ else
+ seqamid = LOCAL_SEQAM_OID;
+
/*
* Create relation (and fill value[] and null[] for the tuple)
*/
@@ -207,11 +221,6 @@ DefineSequence(CreateSeqStmt *seq)
coldef->colname = "cache_value";
value[i - 1] = Int64GetDatumFast(new.cache_value);
break;
- case SEQ_COL_LOG:
- coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
- coldef->colname = "log_cnt";
- value[i - 1] = Int64GetDatum((int64) 0);
- break;
case SEQ_COL_CYCLE:
coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
coldef->colname = "is_cycled";
@@ -222,6 +231,12 @@ DefineSequence(CreateSeqStmt *seq)
coldef->colname = "is_called";
value[i - 1] = BoolGetDatum(false);
break;
+ case SEQ_COL_AMDATA:
+ coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1);
+ coldef->colname = "amdata";
+ null[i - 1] = true;
+ value[i - 1] = (Datum) 0;
+ break;
}
stmt->tableElts = lappend(stmt->tableElts, coldef);
}
@@ -229,12 +244,13 @@ DefineSequence(CreateSeqStmt *seq)
stmt->relation = seq->sequence;
stmt->inhRelations = NIL;
stmt->constraints = NIL;
- stmt->options = NIL;
+ stmt->options = seq->amoptions;
stmt->oncommit = ONCOMMIT_NOOP;
stmt->tablespacename = NULL;
stmt->if_not_exists = seq->if_not_exists;
- address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, NULL);
+ address = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, seqamid,
+ NULL);
seqoid = address.objectId;
Assert(seqoid != InvalidOid);
@@ -243,6 +259,7 @@ DefineSequence(CreateSeqStmt *seq)
/* now initialize the sequence's data */
tuple = heap_form_tuple(tupDesc, value, null);
+ tuple = seqam_init(rel, tuple, new.start_value, false, true);
fill_seq_with_data(rel, tuple);
/* process OWNED BY if given */
@@ -254,6 +271,7 @@ DefineSequence(CreateSeqStmt *seq)
return address;
}
+
/*
* Reset a sequence to its initial value.
*
@@ -267,58 +285,55 @@ DefineSequence(CreateSeqStmt *seq)
* responsible for permissions checking.
*/
void
-ResetSequence(Oid seq_relid)
+ResetSequence(Oid seqrelid)
{
- Relation seq_rel;
- SeqTable elm;
- Form_pg_sequence seq;
- Buffer buf;
- HeapTupleData seqtuple;
HeapTuple tuple;
+ Relation seqrel;
+ SequenceHandle seqh;
+ Form_pg_sequence seq;
/*
- * Read the old sequence. This does a bit more work than really
- * necessary, but it's simple, and we do want to double-check that it's
- * indeed a sequence.
+ * Read and lock the old page.
*/
- init_sequence(seq_relid, &elm, &seq_rel);
- (void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple);
+ sequence_open(seqrelid, &seqh);
+ tuple = sequence_read_tuple(&seqh);
+ seqrel = seqh.rel;
/*
* Copy the existing sequence tuple.
*/
- tuple = heap_copytuple(&seqtuple);
+ tuple = heap_copytuple(tuple);
/* Now we're done with the old page */
- UnlockReleaseBuffer(buf);
+ sequence_release_tuple(&seqh);
/*
- * Modify the copied tuple to execute the restart (compare the RESTART
- * action in AlterSequence)
+ * Tell AM to reset the sequence.
+ * This fakes the ALTER SEQUENCE RESTART command from the
+ * Sequence AM perspective.
*/
seq = (Form_pg_sequence) GETSTRUCT(tuple);
- seq->last_value = seq->start_value;
- seq->is_called = false;
- seq->log_cnt = 0;
+ tuple = seqam_init(seqrel, tuple, seq->start_value, true, false);
/*
* Create a new storage file for the sequence. We want to keep the
* sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
* Same with relminmxid, since a sequence will never contain multixacts.
*/
- RelationSetNewRelfilenode(seq_rel, seq_rel->rd_rel->relpersistence,
+ RelationSetNewRelfilenode(seqrel, seqh.rel->rd_rel->relpersistence,
InvalidTransactionId, InvalidMultiXactId);
/*
* Insert the modified tuple into the new storage file.
*/
- fill_seq_with_data(seq_rel, tuple);
+ fill_seq_with_data(seqrel, tuple);
/* Clear local cache so that we don't think we have cached numbers */
/* Note that we do not change the currval() state */
- elm->cached = elm->last;
+ seqh.elm->cached = seqh.elm->last;
- relation_close(seq_rel, NoLock);
+ /* And we're done, close the sequence. */
+ sequence_close(&seqh);
}
/*
@@ -361,7 +376,13 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
- /* check the comment above nextval_internal()'s equivalent call. */
+ /*
+ * If something needs to be WAL logged, make sure that xid was acquired,
+ * so this transaction's commit will trigger a WAL flush and wait for
+ * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
+ * no need to assign xids subxacts, that'll already trigger a appropriate
+ * wait. (Has to be done outside of critical section).
+ */
if (RelationNeedsWAL(rel))
GetTopTransactionId();
@@ -375,23 +396,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
elog(ERROR, "failed to add sequence tuple to page");
/* XLOG stuff */
- if (RelationNeedsWAL(rel))
- {
- xl_seq_rec xlrec;
- XLogRecPtr recptr;
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
- xlrec.node = rel->rd_node;
-
- XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
- XLogRegisterData((char *) tuple->t_data, tuple->t_len);
-
- recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
- PageSetLSN(page, recptr);
- }
+ log_sequence_tuple(rel, tuple, buf, page);
END_CRIT_SECTION();
@@ -406,19 +411,23 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
ObjectAddress
AlterSequence(AlterSeqStmt *stmt)
{
- Oid relid;
- SeqTable elm;
+ Oid seqrelid;
+ Oid oldamid;
+ Oid seqamid;
+ HeapTuple tuple;
Relation seqrel;
- Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
- FormData_pg_sequence new;
+ Form_pg_sequence new;
List *owned_by;
ObjectAddress address;
+ List *seqoptions;
+ int64 restart_value;
+ bool restart_requested;
+ SequenceHandle seqh;
/* Open and lock sequence. */
- relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
- if (relid == InvalidOid)
+ seqrelid = RangeVarGetRelid(stmt->sequence, AccessExclusiveLock, stmt->missing_ok);
+
+ if (seqrelid == InvalidOid)
{
ereport(NOTICE,
(errmsg("relation \"%s\" does not exist, skipping",
@@ -426,70 +435,173 @@ AlterSequence(AlterSeqStmt *stmt)
return InvalidObjectAddress;
}
- init_sequence(relid, &elm, &seqrel);
+ sequence_open(seqrelid, &seqh);
+ seqrel = seqh.rel;
/* allow ALTER to sequence owner only */
- if (!pg_class_ownercheck(relid, GetUserId()))
+ if (!pg_class_ownercheck(seqrelid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
stmt->sequence->relname);
/* lock page' buffer and read tuple into new sequence structure */
- seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+ tuple = sequence_read_tuple(&seqh);
/* Copy old values of options into workspace */
- memcpy(&new, seq, sizeof(FormData_pg_sequence));
+ tuple = heap_copytuple(tuple);
+ new = (Form_pg_sequence) GETSTRUCT(tuple);
/* Check and set new values */
- init_params(stmt->options, false, &new, &owned_by);
+ seqoptions = stmt->options;
+ init_params(seqoptions, false, new, &owned_by);
- /* Clear local cache so that we don't think we have cached numbers */
- /* Note that we do not change the currval() state */
- elm->cached = elm->last;
+ oldamid = seqrel->rd_rel->relam;
+ if (stmt->accessMethod)
+ seqamid = get_seqam_oid(stmt->accessMethod, false);
+ else
+ seqamid = oldamid;
- /* check the comment above nextval_internal()'s equivalent call. */
- if (RelationNeedsWAL(seqrel))
- GetTopTransactionId();
+ restart_value = sequence_get_restart_value(seqoptions, new->start_value,
+ &restart_requested);
- /* Now okay to update the on-disk tuple */
- START_CRIT_SECTION();
+ /*
+ * If we are changing sequence AM, we need to alter the sequence relation.
+ */
+ if (seqamid != oldamid)
+ {
+ ObjectAddress myself,
+ referenced;
+ Relation pgcrel;
+ HeapTuple pgctup,
+ newpgctuple;
+ Form_pg_seqam seqam;
+ HeapTuple seqamtup;
+ Datum reloptions;
+ Datum values[Natts_pg_class];
+ bool nulls[Natts_pg_class];
+ bool replace[Natts_pg_class];
+ static char *validnsps[2];
- memcpy(seq, &new, sizeof(FormData_pg_sequence));
+ /*
+ * If RESTART [WITH] option was not specified in ALTER SEQUENCE
+ * statement, we use nextval of the old sequence AM to provide
+ * restart point for the new sequence AM.
+ */
+ if (!restart_requested)
+ {
+ int64 last;
+ restart_value = seqam_alloc(seqrel, &seqh, 1, &last);
+ }
- MarkBufferDirty(buf);
+ sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
- /* XLOG stuff */
- if (RelationNeedsWAL(seqrel))
- {
- xl_seq_rec xlrec;
- XLogRecPtr recptr;
- Page page = BufferGetPage(buf);
+ /* We don't need the old sequence tuple anymore. */
+ sequence_release_tuple(&seqh);
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+ /* Parse the new reloptions. */
+ seqamtup = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(seqamid));
+ if (!HeapTupleIsValid(seqamtup))
+ elog(ERROR, "cache lookup failed for sequence access method %u",
+ seqamid);
- xlrec.node = seqrel->rd_node;
- XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+ seqam = (Form_pg_seqam) GETSTRUCT(seqamtup);
- XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
+ validnsps[0] = NameStr(seqam->seqamname);
+ validnsps[1] = NULL;
- recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+ reloptions = transformRelOptions((Datum) 0, stmt->amoptions, NULL,
+ validnsps, true, false);
- PageSetLSN(page, recptr);
- }
+ (void) am_reloptions(seqam->seqamreloptions, reloptions, true);
+ ReleaseSysCache(seqamtup);
- END_CRIT_SECTION();
+ /* Update the pg_class entry. */
+ pgcrel = heap_open(RelationRelationId, RowExclusiveLock);
+ pgctup = SearchSysCache1(RELOID, ObjectIdGetDatum(seqrelid));
+ if (!HeapTupleIsValid(pgctup))
+ elog(ERROR, "pg_class entry for sequence %u unavailable",
+ seqrelid);
- UnlockReleaseBuffer(buf);
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replace, false, sizeof(replace));
+
+ values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(seqamid);
+ replace[Anum_pg_class_relam - 1] = true;
+
+ if (reloptions != (Datum) 0)
+ values[Anum_pg_class_reloptions - 1] = reloptions;
+ else
+ nulls[Anum_pg_class_reloptions - 1] = true;
+ replace[Anum_pg_class_reloptions - 1] = true;
+
+ newpgctuple = heap_modify_tuple(pgctup, RelationGetDescr(pgcrel),
+ values, nulls, replace);
+
+ simple_heap_update(pgcrel, &newpgctuple->t_self, newpgctuple);
+
+ CatalogUpdateIndexes(pgcrel, newpgctuple);
+
+ heap_freetuple(newpgctuple);
+ ReleaseSysCache(pgctup);
+
+ heap_close(pgcrel, NoLock);
+
+ CommandCounterIncrement();
+
+ /* Let the new sequence AM initialize. */
+ tuple = seqam_init(seqrel, tuple, restart_value, true, true);
+
+ /*
+ * Create a new storage file for the sequence.
+ * See ResetSequence for why we do this.
+ */
+ RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+ InvalidTransactionId, InvalidMultiXactId);
+ /*
+ * Insert the modified tuple into the new storage file.
+ */
+ fill_seq_with_data(seqh.rel, tuple);
+
+ /* Remove dependency on previous SeqAM */
+ deleteDependencyRecordsForClass(RelationRelationId, seqrelid,
+ SeqAccessMethodRelationId,
+ DEPENDENCY_NORMAL);
+
+ /* Record dependency on new SeqAM */
+ myself.classId = RelationRelationId;
+ myself.objectId = seqrelid;
+ myself.objectSubId = 0;
+ referenced.classId = SeqAccessMethodRelationId;
+ referenced.objectId = seqamid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+ else
+ {
+ sequence_check_range(restart_value, new->min_value, new->max_value, "RESTART");
+
+ /* Let sequence AM update the tuple. */
+ tuple = seqam_init(seqrel, tuple, restart_value, restart_requested, false);
+ sequence_swap_tuple(&seqh, tuple);
+ sequence_start_update(&seqh, true);
+ sequence_apply_update(&seqh, true);
+ sequence_finish_update(&seqh);
+ sequence_release_tuple(&seqh);
+ }
+
+ /* Clear local cache so that we don't think we have cached numbers */
+ /* Note that we do not change the currval() state */
+ seqh.elm->cached = seqh.elm->last;
/* process OWNED BY if given */
if (owned_by)
process_owned_by(seqrel, owned_by);
- InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
+ InvokeObjectPostAlterHook(RelationRelationId, seqrelid, 0);
- ObjectAddressSet(address, RelationRelationId, relid);
+ ObjectAddressSet(address, RelationRelationId, seqrelid);
- relation_close(seqrel, NoLock);
+ sequence_close(&seqh);
return address;
}
@@ -530,29 +642,24 @@ nextval_oid(PG_FUNCTION_ARGS)
PG_RETURN_INT64(nextval_internal(relid));
}
+/*
+ * Sequence AM independent part of nextval() that does permission checking,
+ * returns cached values and then calls out to the SeqAM specific nextval part.
+ */
static int64
nextval_internal(Oid relid)
{
SeqTable elm;
Relation seqrel;
- Buffer buf;
- Page page;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
- int64 incby,
- maxv,
- minv,
- cache,
- log,
- fetch,
- last;
- int64 result,
- next,
- rescnt = 0;
- bool logit = false;
+ Form_pg_sequence seq_form;
+ int64 last,
+ result;
+ SequenceHandle seqh;
/* open and AccessShareLock sequence */
- init_sequence(relid, &elm, &seqrel);
+ sequence_open(relid, &seqh);
+ elm = seqh.elm;
+ seqrel = seqh.rel;
if (pg_class_aclcheck(elm->relid, GetUserId(),
ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
@@ -570,121 +677,15 @@ nextval_internal(Oid relid)
Assert(elm->last_valid);
Assert(elm->increment != 0);
elm->last += elm->increment;
- relation_close(seqrel, NoLock);
+ sequence_close(&seqh);
last_used_seq = elm;
return elm->last;
}
/* lock page' buffer and read tuple */
- seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
- page = BufferGetPage(buf);
-
- last = next = result = seq->last_value;
- incby = seq->increment_by;
- maxv = seq->max_value;
- minv = seq->min_value;
- fetch = cache = seq->cache_value;
- log = seq->log_cnt;
-
- if (!seq->is_called)
- {
- rescnt++; /* return last_value if not is_called */
- fetch--;
- }
-
- /*
- * Decide whether we should emit a WAL log record. If so, force up the
- * fetch count to grab SEQ_LOG_VALS more values than we actually need to
- * cache. (These will then be usable without logging.)
- *
- * If this is the first nextval after a checkpoint, we must force a new
- * WAL record to be written anyway, else replay starting from the
- * checkpoint would fail to advance the sequence past the logged values.
- * In this case we may as well fetch extra values.
- */
- if (log < fetch || !seq->is_called)
- {
- /* forced log to satisfy local demand for values */
- fetch = log = fetch + SEQ_LOG_VALS;
- logit = true;
- }
- else
- {
- XLogRecPtr redoptr = GetRedoRecPtr();
-
- if (PageGetLSN(page) <= redoptr)
- {
- /* last update of seq was before checkpoint */
- fetch = log = fetch + SEQ_LOG_VALS;
- logit = true;
- }
- }
-
- while (fetch) /* try to fetch cache [+ log ] numbers */
- {
- /*
- * Check MAXVALUE for ascending sequences and MINVALUE for descending
- * sequences
- */
- if (incby > 0)
- {
- /* ascending sequence */
- if ((maxv >= 0 && next > maxv - incby) ||
- (maxv < 0 && next + incby > maxv))
- {
- if (rescnt > 0)
- break; /* stop fetching */
- if (!seq->is_cycled)
- {
- char buf[100];
+ seq_form = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
- snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
- RelationGetRelationName(seqrel), buf)));
- }
- next = minv;
- }
- else
- next += incby;
- }
- else
- {
- /* descending sequence */
- if ((minv < 0 && next < minv - incby) ||
- (minv >= 0 && next + incby < minv))
- {
- if (rescnt > 0)
- break; /* stop fetching */
- if (!seq->is_cycled)
- {
- char buf[100];
-
- snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
- RelationGetRelationName(seqrel), buf)));
- }
- next = maxv;
- }
- else
- next += incby;
- }
- fetch--;
- if (rescnt < cache)
- {
- log--;
- rescnt++;
- last = next;
- if (rescnt == 1) /* if it's first result - */
- result = next; /* it's what to return */
- }
- }
-
- log -= fetch; /* adjust for any unfetched numbers */
- Assert(log >= 0);
+ result = seqam_alloc(seqrel, &seqh, seq_form->cache_value, &last);
/* save info in local cache */
elm->last = result; /* last returned number */
@@ -693,70 +694,8 @@ nextval_internal(Oid relid)
last_used_seq = elm;
- /*
- * If something needs to be WAL logged, acquire an xid, so this
- * transaction's commit will trigger a WAL flush and wait for
- * syncrep. It's sufficient to ensure the toplevel transaction has a xid,
- * no need to assign xids subxacts, that'll already trigger a appropriate
- * wait. (Have to do that here, so we're outside the critical section)
- */
- if (logit && RelationNeedsWAL(seqrel))
- GetTopTransactionId();
-
- /* ready to change the on-disk (or really, in-buffer) tuple */
- START_CRIT_SECTION();
-
- /*
- * We must mark the buffer dirty before doing XLogInsert(); see notes in
- * SyncOneBuffer(). However, we don't apply the desired changes just yet.
- * This looks like a violation of the buffer update protocol, but it is in
- * fact safe because we hold exclusive lock on the buffer. Any other
- * process, including a checkpoint, that tries to examine the buffer
- * contents will block until we release the lock, and then will see the
- * final state that we install below.
- */
- MarkBufferDirty(buf);
-
- /* XLOG stuff */
- if (logit && RelationNeedsWAL(seqrel))
- {
- xl_seq_rec xlrec;
- XLogRecPtr recptr;
-
- /*
- * We don't log the current state of the tuple, but rather the state
- * as it would appear after "log" more fetches. This lets us skip
- * that many future WAL records, at the cost that we lose those
- * sequence values if we crash.
- */
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
- /* set values that will be saved in xlog */
- seq->last_value = next;
- seq->is_called = true;
- seq->log_cnt = 0;
-
- xlrec.node = seqrel->rd_node;
-
- XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
- XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
- recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
- PageSetLSN(page, recptr);
- }
-
- /* Now update sequence tuple to the intended final state */
- seq->last_value = last; /* last fetched number */
- seq->is_called = true;
- seq->log_cnt = log; /* how much is logged */
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buf);
-
- relation_close(seqrel, NoLock);
+ sequence_release_tuple(&seqh);
+ sequence_close(&seqh);
return result;
}
@@ -766,28 +705,27 @@ currval_oid(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
- SeqTable elm;
- Relation seqrel;
+ SequenceHandle seqh;
/* open and AccessShareLock sequence */
- init_sequence(relid, &elm, &seqrel);
+ sequence_open(relid, &seqh);
- if (pg_class_aclcheck(elm->relid, GetUserId(),
+ if (pg_class_aclcheck(seqh.elm->relid, GetUserId(),
ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied for sequence %s",
- RelationGetRelationName(seqrel))));
+ RelationGetRelationName(seqh.rel))));
- if (!elm->last_valid)
+ if (!seqh.elm->last_valid)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("currval of sequence \"%s\" is not yet defined in this session",
- RelationGetRelationName(seqrel))));
+ RelationGetRelationName(seqh.rel))));
- result = elm->last;
+ result = seqh.elm->last;
- relation_close(seqrel, NoLock);
+ sequence_close(&seqh);
PG_RETURN_INT64(result);
}
@@ -828,31 +766,24 @@ lastval(PG_FUNCTION_ARGS)
}
/*
- * Main internal procedure that handles 2 & 3 arg forms of SETVAL.
- *
- * Note that the 3 arg version (which sets the is_called flag) is
- * only for use in pg_dump, and setting the is_called flag may not
- * work if multiple users are attached to the database and referencing
- * the sequence (unlikely if pg_dump is restoring it).
- *
- * It is necessary to have the 3 arg version so that pg_dump can
- * restore the state of a sequence exactly during data-only restores -
- * it is the only way to clear the is_called flag in an existing
- * sequence.
+ * Implement the setval procedure.
*/
-static void
-do_setval(Oid relid, int64 next, bool iscalled)
+Datum
+setval_oid(PG_FUNCTION_ARGS)
{
+ Oid relid = PG_GETARG_OID(0);
+ int64 next = PG_GETARG_INT64(1);
SeqTable elm;
Relation seqrel;
- Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
+ SequenceHandle seqh;
/* open and AccessShareLock sequence */
- init_sequence(relid, &elm, &seqrel);
+ sequence_open(relid, &seqh);
+ elm = seqh.elm;
+ seqrel = seqh.rel;
- if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
+ if (pg_class_aclcheck(elm->relid, GetUserId(),
+ ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied for sequence %s",
@@ -862,92 +793,27 @@ do_setval(Oid relid, int64 next, bool iscalled)
if (!seqrel->rd_islocaltemp)
PreventCommandIfReadOnly("setval()");
- /* lock page' buffer and read tuple */
- seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+ seqam_setval(seqrel, &seqh, next);
- if ((next < seq->min_value) || (next > seq->max_value))
- {
- char bufv[100],
- bufm[100],
- bufx[100];
-
- snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
- snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
- bufv, RelationGetRelationName(seqrel),
- bufm, bufx)));
- }
-
- /* Set the currval() state only if iscalled = true */
- if (iscalled)
- {
- elm->last = next; /* last returned number */
- elm->last_valid = true;
- }
-
- /* In any case, forget any future cached numbers */
+ /* Reset local cached data */
+ elm->last = next; /* last returned number */
+ elm->last_valid = true;
elm->cached = elm->last;
- /* check the comment above nextval_internal()'s equivalent call. */
- if (RelationNeedsWAL(seqrel))
- GetTopTransactionId();
-
- /* ready to change the on-disk (or really, in-buffer) tuple */
- START_CRIT_SECTION();
-
- seq->last_value = next; /* last fetched number */
- seq->is_called = iscalled;
- seq->log_cnt = 0;
-
- MarkBufferDirty(buf);
-
- /* XLOG stuff */
- if (RelationNeedsWAL(seqrel))
- {
- xl_seq_rec xlrec;
- XLogRecPtr recptr;
- Page page = BufferGetPage(buf);
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
- xlrec.node = seqrel->rd_node;
- XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
- XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len);
-
- recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buf);
-
- relation_close(seqrel, NoLock);
-}
-
-/*
- * Implement the 2 arg setval procedure.
- * See do_setval for discussion.
- */
-Datum
-setval_oid(PG_FUNCTION_ARGS)
-{
- Oid relid = PG_GETARG_OID(0);
- int64 next = PG_GETARG_INT64(1);
+ last_used_seq = elm;
- do_setval(relid, next, true);
+ sequence_close(&seqh);
PG_RETURN_INT64(next);
}
/*
* Implement the 3 arg setval procedure.
- * See do_setval for discussion.
+ *
+ * This is a cludge for supporting old dumps.
+ *
+ * Check that the target sequence is local one and then convert this call
+ * to the seqam_restore call with apropriate data.
*/
Datum
setval3_oid(PG_FUNCTION_ARGS)
@@ -955,13 +821,67 @@ setval3_oid(PG_FUNCTION_ARGS)
Oid relid = PG_GETARG_OID(0);
int64 next = PG_GETARG_INT64(1);
bool iscalled = PG_GETARG_BOOL(2);
+ ArrayType *statearr;
+ Datum datums[4];
+ Datum val;
+ SeqTable elm;
+ Relation seqrel;
+ SequenceHandle seqh;
+
+ /* open and AccessShareLock sequence */
+ sequence_open(relid, &seqh);
+ elm = seqh.elm;
+ seqrel = seqh.rel;
+
+ if (pg_class_aclcheck(elm->relid, GetUserId(),
+ ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for sequence %s",
+ RelationGetRelationName(seqrel))));
+
+ /* read-only transactions may only modify temp sequences */
+ if (!seqrel->rd_islocaltemp)
+ PreventCommandIfReadOnly("setval()");
+
+ /* Make sure the target sequence is 'local' sequence. */
+ if (seqrel->rd_rel->relam != LOCAL_SEQAM_OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("the setval(oid, bigint, bool) function can only be called for \"local\" sequences")));
+
+ /* Convert the data into 'local' sequence dump format and call restore API. */
+ datums[0] = CStringGetTextDatum("last_value");
+ val = DirectFunctionCall1(int8out, Int64GetDatum(next));
+ datums[1] = CStringGetTextDatum(DatumGetCString(val));
+
+ datums[2] = CStringGetTextDatum("is_called");
+ val = DirectFunctionCall1(boolout, BoolGetDatum(iscalled));
+ datums[3] = CStringGetTextDatum(DatumGetCString(val));
+
+ statearr = construct_array(datums, 4, TEXTOID, -1, false, 'i');
+
+ seqam_set_state(seqh.rel, &seqh, statearr);
+
+ pfree(statearr);
+
+ /* Set the currval() state only if iscalled = true */
+ if (iscalled)
+ {
+ elm->last = next; /* last returned number */
+ elm->last_valid = true;
+ }
+
+ /* Reset local cached data */
+ elm->cached = elm->last;
+
+ last_used_seq = elm;
- do_setval(relid, next, iscalled);
+ sequence_close(&seqh);
PG_RETURN_INT64(next);
}
-
/*
* Open the sequence and acquire AccessShareLock if needed
*
@@ -1020,11 +940,10 @@ create_seq_hashtable(void)
}
/*
- * Given a relation OID, open and lock the sequence. p_elm and p_rel are
- * output parameters.
+ * Given a relation OID, open and share-lock the sequence.
*/
-static void
-init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
+void
+sequence_open(Oid relid, SequenceHandle *seqh)
{
SeqTable elm;
Relation seqrel;
@@ -1076,44 +995,56 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
}
/* Return results */
- *p_elm = elm;
- *p_rel = seqrel;
+ seqh->elm = elm;
+ seqh->rel = seqrel;
+ seqh->buf = InvalidBuffer;
+ seqh->tup.t_data = NULL;
+ seqh->tup.t_len = 0;
+ seqh->temppage = NULL;
+ seqh->inupdate = false;
}
+/*
+ * Given the sequence handle, unlock the page buffer and close the relation
+ */
+void
+sequence_close(SequenceHandle *seqh)
+{
+ Assert(seqh->temppage == NULL && !seqh->inupdate);
+
+ relation_close(seqh->rel, NoLock);
+}
/*
* Given an opened sequence relation, lock the page buffer and find the tuple
- *
- * *buf receives the reference to the pinned-and-ex-locked buffer
- * *seqtuple receives the reference to the sequence tuple proper
- * (this arg should point to a local variable of type HeapTupleData)
- *
- * Function's return value points to the data payload of the tuple
*/
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+HeapTuple
+sequence_read_tuple(SequenceHandle *seqh)
{
Page page;
+ Buffer buf;
ItemId lp;
sequence_magic *sm;
- Form_pg_sequence seq;
- *buf = ReadBuffer(rel, 0);
- LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
+ if (seqh->tup.t_data != NULL)
+ return &seqh->tup;
- page = BufferGetPage(*buf);
+ seqh->buf = buf = ReadBuffer(seqh->rel, 0);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ page = BufferGetPage(buf);
sm = (sequence_magic *) PageGetSpecialPointer(page);
if (sm->magic != SEQ_MAGIC)
elog(ERROR, "bad magic number in sequence \"%s\": %08X",
- RelationGetRelationName(rel), sm->magic);
+ RelationGetRelationName(seqh->rel), sm->magic);
lp = PageGetItemId(page, FirstOffsetNumber);
Assert(ItemIdIsNormal(lp));
- /* Note we currently only bother to set these two fields of *seqtuple */
- seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- seqtuple->t_len = ItemIdGetLength(lp);
+ /* Note we currently only bother to set these two fields of the tuple */
+ seqh->tup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ seqh->tup.t_len = ItemIdGetLength(lp);
/*
* Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1123,33 +1054,185 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
* bit update, ie, don't bother to WAL-log it, since we can certainly do
* this again if the update gets lost.
*/
- Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
- if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+ Assert(!(seqh->tup.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+ if (HeapTupleHeaderGetRawXmax(seqh->tup.t_data) != InvalidTransactionId)
+ {
+ HeapTupleHeaderSetXmax(seqh->tup.t_data, InvalidTransactionId);
+ seqh->tup.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+ seqh->tup.t_data->t_infomask |= HEAP_XMAX_INVALID;
+ MarkBufferDirtyHint(buf, true);
+ }
+
+ /* update our copy of the increment if needed */
+ if (seqh->elm->increment == 0)
+ {
+ Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(&seqh->tup);
+ seqh->elm->increment = seq->increment_by;
+ }
+
+ return &seqh->tup;
+}
+
+/*
+ * Swap the current working tuple.
+ *
+ * Note that the changes are only saved to in-memory state and will not be
+ * visible unless the sequence_*_update sequence is called.
+ */
+void
+sequence_swap_tuple(SequenceHandle *seqh, HeapTuple newtup)
+{
+ Page page;
+
+ Assert(!seqh->inupdate && seqh->tup.t_data != NULL &&
+ seqh->temppage == NULL);
+
+ page = BufferGetPage(seqh->buf);
+
+ seqh->temppage = PageGetTempPageCopySpecial(page);
+
+ /* Sequence tuples are always frozen. */
+ HeapTupleHeaderSetXmin(newtup->t_data, FrozenTransactionId);
+ HeapTupleHeaderSetXminFrozen(newtup->t_data);
+ HeapTupleHeaderSetCmin(newtup->t_data, FirstCommandId);
+ HeapTupleHeaderSetXmax(newtup->t_data, InvalidTransactionId);
+ newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+ ItemPointerSet(&newtup->t_data->t_ctid, 0, FirstOffsetNumber);
+
+ if (PageAddItem(seqh->temppage, (Item) newtup->t_data, newtup->t_len,
+ FirstOffsetNumber, false, false) == InvalidOffsetNumber)
+ elog(PANIC, "sequence_apply_update: failed to add item to page");
+
+ PageSetLSN(seqh->temppage, PageGetLSN(page));
+
+ seqh->tup.t_data = newtup->t_data;
+ seqh->tup.t_len = newtup->t_len;
+}
+
+/*
+ * Write a sequence tuple.
+ *
+ * If 'do_wal' is false, the update doesn't need to be WAL-logged. After
+ * a crash, you might get an old copy of the tuple.
+ *
+ * We split this into 3 step process so that the tuple may be safely updated
+ * inline.
+ */
+void
+sequence_start_update(SequenceHandle *seqh, bool do_wal)
+{
+ Assert(seqh->tup.t_data != NULL && !seqh->inupdate);
+
+ if (do_wal)
+ GetTopTransactionId();
+
+ seqh->inupdate = true;
+
+ START_CRIT_SECTION();
+}
+
+void
+sequence_apply_update(SequenceHandle *seqh, bool do_wal)
+{
+ Page page;
+
+ Assert(seqh->inupdate && seqh->tup.t_data != NULL);
+
+ page = BufferGetPage(seqh->buf);
+
+ /*
+ * If the working tuple was swapped we need to copy it to the page
+ */
+ if (seqh->temppage != NULL)
+ {
+ PageRestoreTempPage(seqh->temppage, page);
+ seqh->temppage = NULL;
+ }
+
+ MarkBufferDirtyHint(seqh->buf, true);
+
+ if (do_wal)
+ log_sequence_tuple(seqh->rel, &seqh->tup, seqh->buf, page);
+}
+
+void
+sequence_finish_update(SequenceHandle *seqh)
+{
+ Assert(seqh->inupdate && seqh->temppage == NULL);
+
+ END_CRIT_SECTION();
+
+ seqh->inupdate = false;
+}
+
+
+/*
+ * Release a tuple, read with sequence_read_tuple, without saving it
+ */
+void
+sequence_release_tuple(SequenceHandle *seqh)
+{
+ /* Remove the tuple from cache */
+ if (seqh->tup.t_data != NULL)
+ {
+ seqh->tup.t_data = NULL;
+ seqh->tup.t_len = 0;
+ }
+
+ if (seqh->temppage)
+ {
+ pfree(seqh->temppage);
+ seqh->temppage = NULL;
+ }
+
+ /* Release the page lock */
+ if (BufferIsValid(seqh->buf))
{
- HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
- seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
- seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
- MarkBufferDirtyHint(*buf, true);
+ UnlockReleaseBuffer(seqh->buf);
+ seqh->buf = InvalidBuffer;
}
+}
+
+/*
+ * Returns true, if the next update to the sequence tuple needs to be
+ * WAL-logged because it's the first update after a checkpoint.
+ *
+ * The sequence AM can use this as a hint, if it wants to piggyback some extra
+ * actions on WAL-logged updates.
+ *
+ * NB: This is just a hint. even when sequence_needs_wal() returns 'false',
+ * the sequence access method might decide to WAL-log an update anyway.
+ */
+bool
+sequence_needs_wal(SequenceHandle *seqh)
+{
+ Page page;
+ XLogRecPtr redoptr;
+
+ Assert(BufferIsValid(seqh->buf));
- seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
+ if (!RelationNeedsWAL(seqh->rel))
+ return false;
- /* this is a handy place to update our copy of the increment */
- elm->increment = seq->increment_by;
+ page = BufferGetPage(seqh->buf);
+ redoptr = GetRedoRecPtr();
- return seq;
+ return (PageGetLSN(page) <= redoptr);
}
/*
- * init_params: process the options list of CREATE or ALTER SEQUENCE,
+ * init_params: process the params list of CREATE or ALTER SEQUENCE,
* and store the values into appropriate fields of *new. Also set
- * *owned_by to any OWNED BY option, or to NIL if there is none.
+ * *owned_by to any OWNED BY param, or to NIL if there is none.
*
- * If isInit is true, fill any unspecified options with default values;
- * otherwise, do not change existing options that aren't explicitly overridden.
+ * If isInit is true, fill any unspecified params with default values;
+ * otherwise, do not change existing params that aren't explicitly overridden.
+ *
+ * Note that only syntax check is done for RESTART [WITH] parameter, the actual
+ * handling of it should be done by init function of a sequence access method.
*/
static void
-init_params(List *options, bool isInit,
+init_params(List *params, bool isInit,
Form_pg_sequence new, List **owned_by)
{
DefElem *start_value = NULL;
@@ -1159,13 +1242,13 @@ init_params(List *options, bool isInit,
DefElem *min_value = NULL;
DefElem *cache_value = NULL;
DefElem *is_cycled = NULL;
- ListCell *option;
+ ListCell *param;
*owned_by = NIL;
- foreach(option, options)
+ foreach(param, params)
{
- DefElem *defel = (DefElem *) lfirst(option);
+ DefElem *defel = (DefElem *) lfirst(param);
if (strcmp(defel->defname, "increment") == 0)
{
@@ -1236,13 +1319,6 @@ init_params(List *options, bool isInit,
defel->defname);
}
- /*
- * We must reset log_cnt when isInit or when changing any parameters that
- * would affect future nextval allocations.
- */
- if (isInit)
- new->log_cnt = 0;
-
/* INCREMENT BY */
if (increment_by != NULL)
{
@@ -1251,7 +1327,6 @@ init_params(List *options, bool isInit,
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("INCREMENT must not be zero")));
- new->log_cnt = 0;
}
else if (isInit)
new->increment_by = 1;
@@ -1261,7 +1336,6 @@ init_params(List *options, bool isInit,
{
new->is_cycled = intVal(is_cycled->arg);
Assert(BoolIsValid(new->is_cycled));
- new->log_cnt = 0;
}
else if (isInit)
new->is_cycled = false;
@@ -1270,7 +1344,6 @@ init_params(List *options, bool isInit,
if (max_value != NULL && max_value->arg)
{
new->max_value = defGetInt64(max_value);
- new->log_cnt = 0;
}
else if (isInit || max_value != NULL)
{
@@ -1278,14 +1351,12 @@ init_params(List *options, bool isInit,
new->max_value = SEQ_MAXVALUE; /* ascending seq */
else
new->max_value = -1; /* descending seq */
- new->log_cnt = 0;
}
/* MINVALUE (null arg means NO MINVALUE) */
if (min_value != NULL && min_value->arg)
{
new->min_value = defGetInt64(min_value);
- new->log_cnt = 0;
}
else if (isInit || min_value != NULL)
{
@@ -1293,7 +1364,6 @@ init_params(List *options, bool isInit,
new->min_value = 1; /* ascending seq */
else
new->min_value = SEQ_MINVALUE; /* descending seq */
- new->log_cnt = 0;
}
/* crosscheck min/max */
@@ -1347,48 +1417,6 @@ init_params(List *options, bool isInit,
bufs, bufm)));
}
- /* RESTART [WITH] */
- if (restart_value != NULL)
- {
- if (restart_value->arg != NULL)
- new->last_value = defGetInt64(restart_value);
- else
- new->last_value = new->start_value;
- new->is_called = false;
- new->log_cnt = 0;
- }
- else if (isInit)
- {
- new->last_value = new->start_value;
- new->is_called = false;
- }
-
- /* crosscheck RESTART (or current value, if changing MIN/MAX) */
- if (new->last_value < new->min_value)
- {
- char bufs[100],
- bufm[100];
-
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
- bufs, bufm)));
- }
- if (new->last_value > new->max_value)
- {
- char bufs[100],
- bufm[100];
-
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
- bufs, bufm)));
- }
-
/* CACHE */
if (cache_value != NULL)
{
@@ -1403,7 +1431,6 @@ init_params(List *options, bool isInit,
errmsg("CACHE (%s) must be greater than zero",
buf)));
}
- new->log_cnt = 0;
}
else if (isInit)
new->cache_value = 1;
@@ -1514,20 +1541,17 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
Datum values[5];
bool isnull[5];
- SeqTable elm;
- Relation seqrel;
- Buffer buf;
- HeapTupleData seqtuple;
Form_pg_sequence seq;
+ SequenceHandle seqh;
/* open and AccessShareLock sequence */
- init_sequence(relid, &elm, &seqrel);
+ sequence_open(relid, &seqh);
if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied for sequence %s",
- RelationGetRelationName(seqrel))));
+ RelationGetRelationName(seqh.rel))));
tupdesc = CreateTemplateTupleDesc(5, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1545,7 +1569,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
memset(isnull, 0, sizeof(isnull));
- seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+ seq = (Form_pg_sequence) GETSTRUCT(sequence_read_tuple(&seqh));
values[0] = Int64GetDatum(seq->start_value);
values[1] = Int64GetDatum(seq->min_value);
@@ -1553,12 +1577,89 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
values[3] = Int64GetDatum(seq->increment_by);
values[4] = BoolGetDatum(seq->is_cycled);
- UnlockReleaseBuffer(buf);
- relation_close(seqrel, NoLock);
+ sequence_release_tuple(&seqh);
+ sequence_close(&seqh);
return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
}
+Datum
+pg_sequence_get_state(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ ArrayType *statearr;
+ SequenceHandle seqh;
+
+ sequence_open(relid, &seqh);
+
+ statearr = seqam_get_state(seqh.rel, &seqh);
+
+ sequence_close(&seqh);
+
+ Assert(ARR_ELEMTYPE(statearr) == TEXTOID && ARR_NDIM(statearr) == 1 &&
+ (ARR_DIMS(statearr)[0]) % 2 == 0 && !ARR_HASNULL(statearr));
+
+ PG_RETURN_ARRAYTYPE_P(statearr);
+}
+
+Datum
+pg_sequence_set_state(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ ArrayType *statearr = PG_GETARG_ARRAYTYPE_P(1);
+ SequenceHandle seqh;
+
+ Assert(ARR_ELEMTYPE(statearr) == TEXTOID);
+
+ /*
+ * Do the input checks.
+ */
+ if (ARR_NDIM(statearr) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("state must be one dimensional array")));
+
+ if ((ARR_DIMS(statearr)[0]) % 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("state array must have even number of elements")));
+
+ if (array_contains_nulls(statearr))
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("state array cannot contain NULLs")));
+
+ /* Call in the sequence. */
+ sequence_open(relid, &seqh);
+
+ seqam_set_state(seqh.rel, &seqh, statearr);
+
+ sequence_close(&seqh);
+
+ PG_FREE_IF_COPY(statearr, 1);
+
+ PG_RETURN_VOID();
+}
+
+static void
+log_sequence_tuple(Relation seqrel, HeapTuple tuple,
+ Buffer buf, Page page)
+{
+ xl_seq_rec xlrec;
+ XLogRecPtr recptr;
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
+
+ xlrec.node = seqrel->rd_node;
+
+ XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
+ XLogRegisterData((char *) tuple->t_data, tuple->t_len);
+
+ recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+
+ PageSetLSN(page, recptr);
+}
void
seq_redo(XLogReaderState *record)
@@ -1624,3 +1725,149 @@ ResetSequenceCaches(void)
last_used_seq = NULL;
}
+
+/*
+ * Increment sequence while correctly handling overflows and min/max.
+ */
+int64
+sequence_increment(Relation seqrel, int64 *value, int64 incnum, int64 minv,
+ int64 maxv, int64 incby, bool is_cycled, bool report_errors)
+{
+ int64 next = *value;
+ int64 rescnt = 0;
+
+ while (incnum)
+ {
+ /*
+ * Check MAXVALUE for ascending sequences and MINVALUE for descending
+ * sequences
+ */
+ if (incby > 0)
+ {
+ /* ascending sequence */
+ if ((maxv >= 0 && next > maxv - incby) ||
+ (maxv < 0 && next + incby > maxv))
+ {
+ /*
+ * We were asked to not report errors, return without
+ * incrementing and let the caller handle it.
+ */
+ if (!report_errors)
+ return rescnt;
+ if (!is_cycled)
+ {
+ char buf[100];
+
+ snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
+ RelationGetRelationName(seqrel), buf)));
+ }
+ next = minv;
+ }
+ else
+ next += incby;
+ }
+ else
+ {
+ /* descending sequence */
+ if ((minv < 0 && next < minv - incby) ||
+ (minv >= 0 && next + incby < minv))
+ {
+ /*
+ * We were asked to not report errors, return without incrementing
+ * and let the caller handle it.
+ */
+ if (!report_errors)
+ return rescnt;
+ if (!is_cycled)
+ {
+ char buf[100];
+
+ snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
+ RelationGetRelationName(seqrel), buf)));
+ }
+ next = maxv;
+ }
+ else
+ next += incby;
+ }
+ rescnt++;
+ incnum--;
+ }
+
+ *value = next;
+
+ return rescnt;
+}
+
+
+/*
+ * Check that new value, minimum and maximum are valid.
+ *
+ * Used by sequence AMs during sequence initialization to validate
+ * the sequence parameters.
+ */
+void
+sequence_check_range(int64 value, int64 min_value, int64 max_value, const char *valname)
+{
+ if (value < min_value)
+ {
+ char bufs[100],
+ bufm[100];
+
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, min_value);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("%s value (%s) cannot be less than MINVALUE (%s)",
+ valname, bufs, bufm)));
+ }
+
+ if (value > max_value)
+ {
+ char bufs[100],
+ bufm[100];
+
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, max_value);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("%s value (%s) cannot be greater than MAXVALUE (%s)",
+ valname, bufs, bufm)));
+ }
+
+}
+
+/*
+ * It's reasonable to expect many sequence AMs to care only about
+ * RESTART [WITH] option of ALTER SEQUENCE command, so we provide
+ * this interface for convenience.
+ * It is also useful for ALTER SEQUENCE USING.
+ */
+int64
+sequence_get_restart_value(List *options, int64 default_value, bool *found)
+{
+ ListCell *opt;
+
+ foreach(opt, options)
+ {
+ DefElem *defel = (DefElem *) lfirst(opt);
+
+ if (strcmp(defel->defname, "restart") == 0)
+ {
+ *found = true;
+ if (defel->arg != NULL)
+ return defGetInt64(defel);
+ else
+ return default_value;
+ }
+ }
+
+ *found = false;
+ return default_value;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index bedd8ae..dcc9040 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -266,6 +266,7 @@ struct DropRelationCallbackState
#define ATT_INDEX 0x0008
#define ATT_COMPOSITE_TYPE 0x0010
#define ATT_FOREIGN_TABLE 0x0020
+#define ATT_SEQUENCE 0x0040
static void truncate_check_rel(Relation rel);
static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -446,7 +447,7 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
* ----------------------------------------------------------------
*/
ObjectAddress
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid relamid,
ObjectAddress *typaddress)
{
char relname[NAMEDATALEN];
@@ -465,7 +466,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -543,13 +543,43 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/*
* Parse and validate reloptions, if any.
*/
- reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
- true, false);
+ if (relkind == RELKIND_SEQUENCE)
+ {
+ HeapTuple tuple;
+ Form_pg_seqam seqam;
+ static char *validnsps[2];
+
+ Assert(relamid != InvalidOid);
+
+ tuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(relamid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for sequence access method %u",
+ relamid);
+
+ seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+
+ validnsps[0] = NameStr(seqam->seqamname);
+ validnsps[1] = NULL;
+
+ reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+ validnsps, true, false);
+
+ (void) am_reloptions(seqam->seqamreloptions, reloptions, true);
- if (relkind == RELKIND_VIEW)
- (void) view_reloptions(reloptions, true);
+ ReleaseSysCache(tuple);
+ }
else
- (void) heap_reloptions(relkind, reloptions, true);
+ {
+ static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+ Assert(relamid == InvalidOid);
+ reloptions = transformRelOptions((Datum) 0, stmt->options, NULL,
+ validnsps, true, false);
+ if (relkind == RELKIND_VIEW)
+ (void) view_reloptions(reloptions, true);
+ else
+ (void) heap_reloptions(relkind, reloptions, true);
+ }
if (stmt->ofTypename)
{
@@ -670,6 +700,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
true,
allowSystemTableMods,
false,
+ relamid,
typaddress);
/* Store inheritance information for new rel. */
@@ -3296,7 +3327,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
case AT_ReplaceRelOptions: /* reset them all, then set just these */
- ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
+ ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE);
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
@@ -4279,6 +4310,9 @@ ATSimplePermissions(Relation rel, int allowed_targets)
case RELKIND_FOREIGN_TABLE:
actual_target = ATT_FOREIGN_TABLE;
break;
+ case RELKIND_SEQUENCE:
+ actual_target = ATT_SEQUENCE;
+ break;
default:
actual_target = 0;
break;
@@ -4319,8 +4353,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
case ATT_TABLE | ATT_VIEW:
msg = _("\"%s\" is not a table or view");
break;
- case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
- msg = _("\"%s\" is not a table, view, materialized view, or index");
+ case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_SEQUENCE:
+ msg = _("\"%s\" is not a table, view, materialized view, index or sequence");
break;
case ATT_TABLE | ATT_MATVIEW:
msg = _("\"%s\" is not a table or materialized view");
@@ -9248,7 +9282,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
(void) view_reloptions(newOptions, true);
break;
case RELKIND_INDEX:
- (void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+ (void) am_reloptions(rel->rd_am->amoptions, newOptions, true);
+ break;
+ case RELKIND_SEQUENCE:
+ (void) am_reloptions(rel->rd_seqam->seqamreloptions, newOptions, true);
break;
default:
ereport(ERROR,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index ab13be2..a69ad61 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2112,7 +2112,8 @@ DefineCompositeType(RangeVar *typevar, List *coldeflist)
/*
* Finally create the relation. This also creates the type.
*/
- DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address);
+ DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, InvalidOid,
+ &address);
return address;
}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index efa4be1..3cea360 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -240,7 +240,8 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
* existing view, so we don't need more code to complain if "replace"
* is false).
*/
- address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, NULL);
+ address = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid,
+ InvalidOid, NULL);
Assert(address.objectId != InvalidOid);
return address;
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1685efe..314f7e1 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3372,7 +3372,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
COPY_NODE_FIELD(sequence);
COPY_NODE_FIELD(options);
+ COPY_NODE_FIELD(amoptions);
COPY_SCALAR_FIELD(ownerId);
+ COPY_STRING_FIELD(accessMethod);
COPY_SCALAR_FIELD(if_not_exists);
return newnode;
@@ -3385,7 +3387,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
COPY_NODE_FIELD(sequence);
COPY_NODE_FIELD(options);
+ COPY_NODE_FIELD(amoptions);
COPY_SCALAR_FIELD(missing_ok);
+ COPY_STRING_FIELD(accessMethod);
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 578ead5..7c0a51e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1563,7 +1563,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
{
COMPARE_NODE_FIELD(sequence);
COMPARE_NODE_FIELD(options);
+ COMPARE_NODE_FIELD(amoptions);
COMPARE_SCALAR_FIELD(ownerId);
+ COMPARE_STRING_FIELD(accessMethod);
COMPARE_SCALAR_FIELD(if_not_exists);
return true;
@@ -1574,7 +1576,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
{
COMPARE_NODE_FIELD(sequence);
COMPARE_NODE_FIELD(options);
+ COMPARE_NODE_FIELD(amoptions);
COMPARE_SCALAR_FIELD(missing_ok);
+ COMPARE_STRING_FIELD(accessMethod);
return true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 962a69d..500c05e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
#include "catalog/index.h"
#include "catalog/namespace.h"
+#include "catalog/pg_seqam.h"
#include "catalog/pg_trigger.h"
#include "commands/defrem.h"
#include "commands/trigger.h"
@@ -3538,7 +3539,33 @@ CreateSeqStmt:
CreateSeqStmt *n = makeNode(CreateSeqStmt);
$4->relpersistence = $2;
n->sequence = $4;
+ n->accessMethod = NULL;
n->options = $5;
+ n->amoptions = NIL;
+ n->ownerId = InvalidOid;
+ $$ = (Node *)n;
+ }
+ | CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+ USING access_method
+ {
+ CreateSeqStmt *n = makeNode(CreateSeqStmt);
+ $4->relpersistence = $2;
+ n->sequence = $4;
+ n->accessMethod = $7;
+ n->options = $5;
+ n->amoptions = NIL;
+ n->ownerId = InvalidOid;
+ $$ = (Node *)n;
+ }
+ | CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
+ USING access_method WITH reloptions
+ {
+ CreateSeqStmt *n = makeNode(CreateSeqStmt);
+ $4->relpersistence = $2;
+ n->sequence = $4;
+ n->accessMethod = $7;
+ n->options = $5;
+ n->amoptions = $9;
n->ownerId = InvalidOid;
n->if_not_exists = false;
$$ = (Node *)n;
@@ -3560,7 +3587,31 @@ AlterSeqStmt:
{
AlterSeqStmt *n = makeNode(AlterSeqStmt);
n->sequence = $3;
+ n->accessMethod = NULL;
+ n->options = $4;
+ n->amoptions = NIL;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER SEQUENCE qualified_name OptSeqOptList
+ USING access_method
+ {
+ AlterSeqStmt *n = makeNode(AlterSeqStmt);
+ n->sequence = $3;
+ n->accessMethod = $6;
n->options = $4;
+ n->amoptions = NIL;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER SEQUENCE qualified_name OptSeqOptList
+ USING access_method WITH reloptions
+ {
+ AlterSeqStmt *n = makeNode(AlterSeqStmt);
+ n->sequence = $3;
+ n->accessMethod = $6;
+ n->options = $4;
+ n->amoptions = $8;
n->missing_ok = false;
$$ = (Node *)n;
}
@@ -3568,11 +3619,34 @@ AlterSeqStmt:
{
AlterSeqStmt *n = makeNode(AlterSeqStmt);
n->sequence = $5;
+ n->accessMethod = NULL;
n->options = $6;
+ n->amoptions = NIL;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+ | ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+ USING access_method
+ {
+ AlterSeqStmt *n = makeNode(AlterSeqStmt);
+ n->sequence = $5;
+ n->accessMethod = $8;
+ n->options = $6;
+ n->amoptions = NIL;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+ | ALTER SEQUENCE IF_P EXISTS qualified_name OptSeqOptList
+ USING access_method WITH reloptions
+ {
+ AlterSeqStmt *n = makeNode(AlterSeqStmt);
+ n->sequence = $5;
+ n->accessMethod = $8;
+ n->options = $6;
+ n->amoptions = $10;
n->missing_ok = true;
$$ = (Node *)n;
}
-
;
OptSeqOptList: SeqOptList { $$ = $1; }
@@ -3631,7 +3705,7 @@ SeqOptElem: CACHE NumericOnly
{
$$ = makeDefElem("restart", (Node *)$3);
}
- ;
+ ;
opt_by: BY {}
| /* empty */ {}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 73924ae..c68b773 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -28,6 +28,7 @@
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/seqam.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
@@ -415,6 +416,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
seqstmt = makeNode(CreateSeqStmt);
seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
seqstmt->options = NIL;
+ seqstmt->amoptions = NIL;
+ seqstmt->accessMethod = serial_seqam;
/*
* If this is ALTER ADD COLUMN, make sure the sequence will be owned
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 31e9d4c..e726831 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -932,7 +932,8 @@ ProcessUtilitySlow(Node *parsetree,
/* Create the table itself */
address = DefineRelation((CreateStmt *) stmt,
RELKIND_RELATION,
- InvalidOid, NULL);
+ InvalidOid, InvalidOid,
+ NULL);
/*
* Let NewRelationCreateToastTable decide if this
@@ -962,7 +963,8 @@ ProcessUtilitySlow(Node *parsetree,
/* Create the table itself */
address = DefineRelation((CreateStmt *) stmt,
RELKIND_FOREIGN_TABLE,
- InvalidOid, NULL);
+ InvalidOid, InvalidOid,
+ NULL);
CreateForeignTable((CreateForeignTableStmt *) stmt,
address.objectId);
}
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 1af43c6..ccb9791 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1059,10 +1059,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
case AMOID:
case AMNAME:
+ case SEQAMOID:
+ case SEQAMNAME:
/*
- * Always do heap scans in pg_am, because it's so small there's
- * not much point in an indexscan anyway. We *must* do this when
+ * Always do heap scans in pg_am and pg_seqam, because they are
+ * too small to benefit from an indexscan. We *must* do this when
* initially building critical relcache entries, but we might as
* well just always do it.
*/
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index e745006..168bee7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -51,6 +51,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
+#include "catalog/pg_seqam.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -266,6 +267,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
static void RelationBuildTupleDesc(Relation relation);
static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
static void RelationInitPhysicalAddr(Relation relation);
+static void RelationInitSequenceAccessInfo(Relation relation);
static void load_critical_index(Oid indexoid, Oid heapoid);
static TupleDesc GetPgClassDescriptor(void);
static TupleDesc GetPgIndexDescriptor(void);
@@ -429,6 +431,7 @@ static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
bytea *options;
+ Oid amoptions = InvalidOid;
relation->rd_options = NULL;
@@ -437,10 +440,15 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
- case RELKIND_INDEX:
case RELKIND_VIEW:
case RELKIND_MATVIEW:
break;
+ case RELKIND_INDEX:
+ amoptions = relation->rd_am->amoptions;
+ break;
+ case RELKIND_SEQUENCE:
+ amoptions = relation->rd_seqam->seqamreloptions;
+ break;
default:
return;
}
@@ -452,8 +460,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
*/
options = extractRelOptions(tuple,
GetPgClassDescriptor(),
- relation->rd_rel->relkind == RELKIND_INDEX ?
- relation->rd_am->amoptions : InvalidOid);
+ amoptions);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
@@ -1056,11 +1063,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
else
relation->rd_rsdesc = NULL;
- /*
- * if it's an index, initialize index-related information
- */
- if (OidIsValid(relation->rd_rel->relam))
+ /* if it's an index, initialize index-related information */
+ if (relation->rd_rel->relkind == RELKIND_INDEX &&
+ OidIsValid(relation->rd_rel->relam))
RelationInitIndexAccessInfo(relation);
+ /* same for sequences */
+ else if (relation->rd_rel->relkind == RELKIND_SEQUENCE &&
+ OidIsValid(relation->rd_rel->relam))
+ RelationInitSequenceAccessInfo(relation);
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1535,6 +1545,36 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Initialize sequence-access-method support data for a sequence relation
+ */
+static void
+RelationInitSequenceAccessInfo(Relation rel)
+{
+ HeapTuple amtuple;
+ Form_pg_seqam amform;
+
+ /*
+ * Make a copy of the pg_seqam entry for the sequence's access method
+ */
+ amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam));
+ if (!HeapTupleIsValid(amtuple))
+ elog(ERROR, "cache lookup failed for sequence access method %u",
+ rel->rd_rel->relam);
+ amform = (Form_pg_seqam) MemoryContextAlloc(CacheMemoryContext,
+ sizeof(*amform));
+ memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform));
+ ReleaseSysCache(amtuple);
+ rel->rd_seqam = amform;
+
+ /*
+ * Initialize sequence AM info struct. (It's left as zeroes, and filled
+ * on-the-fly when used.)
+ */
+ rel->rd_seqaminfo = (RelationSeqAmInfo *)
+ MemoryContextAllocZero(CacheMemoryContext,
+ sizeof(RelationSeqAmInfo));
+}
/*
* formrdesc
@@ -2021,6 +2061,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_indextuple);
if (relation->rd_am)
pfree(relation->rd_am);
+ if (relation->rd_seqam)
+ pfree(relation->rd_seqam);
+ if (relation->rd_seqaminfo)
+ pfree(relation->rd_seqaminfo);
if (relation->rd_indexcxt)
MemoryContextDelete(relation->rd_indexcxt);
if (relation->rd_rulescxt)
@@ -4840,6 +4884,8 @@ load_relcache_init_file(bool shared)
Assert(rel->rd_supportinfo == NULL);
Assert(rel->rd_indoption == NULL);
Assert(rel->rd_indcollation == NULL);
+ Assert(rel->rd_seqam == NULL);
+ Assert(rel->rd_seqaminfo == NULL);
}
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 644bbcc..38f0d35 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -54,6 +54,7 @@
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
+#include "catalog/pg_seqam.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_transform.h"
@@ -632,6 +633,28 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {SeqAccessMethodRelationId, /* SEQAMNAME */
+ SeqAMNameIndexId,
+ 1,
+ {
+ Anum_pg_seqam_seqamname,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {SeqAccessMethodRelationId, /* SEQAMOID */
+ SeqAMOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{StatisticRelationId, /* STATRELATTINH */
StatisticRelidAttnumInhIndexId,
3,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f43aff2..1d31cea 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -28,6 +28,7 @@
#include "access/commit_ts.h"
#include "access/gin.h"
+#include "access/seqam.h"
#include "access/transam.h"
#include "access/twophase.h"
#include "access/xact.h"
@@ -2894,6 +2895,17 @@ static struct config_string ConfigureNamesString[] =
},
{
+ {"serial_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the default sequence access method for SERIAL and BIGSERIAL column types."),
+ gettext_noop("Defaults to 'local' sequence access method."),
+ GUC_IS_NAME
+ },
+ &serial_seqam,
+ "local",
+ check_serial_seqam, NULL, NULL
+ },
+
+ {
{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 110983f..a90637b 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -512,6 +512,7 @@
#default_tablespace = '' # a tablespace name, '' uses the default
#temp_tablespaces = '' # a list of tablespace names, '' uses
# only default tablespace
+#serial_sequenceam = 'local' # default sequence access method for SERIAL
#check_function_bodies = on
#default_transaction_isolation = 'read committed'
#default_transaction_read_only = off
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 977b72e..573b1de 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -51,6 +51,7 @@
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_seqam.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "libpq/libpq-fs.h"
@@ -4534,6 +4535,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
int i_relreplident;
int i_owning_tab;
int i_owning_col;
+ int i_relam;
int i_reltablespace;
int i_reloptions;
int i_checkoption;
@@ -4585,6 +4587,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
+ "c.relam, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4626,6 +4629,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
+ "c.relam, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4667,6 +4671,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
+ "c.relam, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
@@ -4708,6 +4713,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
+ "c.relam, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(c.reloptions, ', ') AS reloptions, "
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4747,6 +4753,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
+ "c.relam, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(c.reloptions, ', ') AS reloptions, "
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4785,6 +4792,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
+ "c.relam, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(c.reloptions, ', ') AS reloptions, "
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
@@ -4823,6 +4831,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
+ "c.relam, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(c.reloptions, ', ') AS reloptions, "
"NULL AS toast_reloptions "
@@ -4861,6 +4870,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
+ "c.relam, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
@@ -4898,6 +4908,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
"d.refobjsubid AS owning_col, "
+ "c.relam, "
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
@@ -4931,6 +4942,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"NULL AS reloftype, "
"NULL::oid AS owning_tab, "
"NULL::int4 AS owning_col, "
+ "c.relam, "
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
@@ -4959,6 +4971,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"NULL AS reloftype, "
"NULL::oid AS owning_tab, "
"NULL::int4 AS owning_col, "
+ "c.relam, "
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
@@ -4997,6 +5010,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
"NULL AS reloftype, "
"NULL::oid AS owning_tab, "
"NULL::int4 AS owning_col, "
+ "c.relam, "
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
@@ -5049,6 +5063,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
i_relpages = PQfnumber(res, "relpages");
i_owning_tab = PQfnumber(res, "owning_tab");
i_owning_col = PQfnumber(res, "owning_col");
+ i_relam = PQfnumber(res, "relam");
i_reltablespace = PQfnumber(res, "reltablespace");
i_reloptions = PQfnumber(res, "reloptions");
i_checkoption = PQfnumber(res, "checkoption");
@@ -5113,6 +5128,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
}
+ if (PQgetisnull(res, i, i_relam))
+ tblinfo[i].relam = InvalidOid;
+ else
+ tblinfo[i].relam = atoi(PQgetvalue(res, i, i_relam));
tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
@@ -14802,7 +14821,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
*incby,
*maxv = NULL,
*minv = NULL,
- *cache;
+ *cache,
+ *amname = NULL;
char bufm[100],
bufx[100];
bool cycled;
@@ -14882,6 +14902,37 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
/*
+ * 9.5 adds sequence access methods but we only care if valid
+ * sequence am that is not the default one is specified.
+ */
+ if (fout->remoteVersion >= 90500 &&
+ tbinfo->relam != InvalidOid &&
+ tbinfo->relam != LOCAL_SEQAM_OID)
+ {
+ PGresult *res2;
+
+ printfPQExpBuffer(query, "SELECT a.seqamname\n"
+ "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+ "WHERE c.relam = a.oid AND c.oid = %u",
+ tbinfo->dobj.catId.oid);
+
+ res2 = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ if (PQntuples(res2) != 1)
+ {
+ write_msg(NULL, ngettext("query to get access method of sequence \"%s\" returned %d row (expected 1)\n",
+ "query to get access method of sequence \"%s\" returned %d rows (expected 1)\n",
+ PQntuples(res2)),
+ tbinfo->dobj.name, PQntuples(res2));
+ exit_nicely(1);
+ }
+
+ amname = pg_strdup(PQgetvalue(res2, 0, 0));
+
+ PQclear(res2);
+ }
+
+ /*
* DROP must be fully qualified in case same name appears in pg_catalog
*/
appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
@@ -14922,6 +14973,13 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
" CACHE %s%s",
cache, (cycled ? "\n CYCLE" : ""));
+ /*
+ * Only produce using when it makes sense,
+ * this helps with backwards compatibility.
+ */
+ if (amname)
+ appendPQExpBuffer(query, "\n USING %s", fmtId(amname));
+
appendPQExpBufferStr(query, ";\n");
appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
@@ -14988,6 +15046,9 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
+ if (amname)
+ free(amname);
+
PQclear(res);
destroyPQExpBuffer(query);
@@ -15004,16 +15065,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
{
TableInfo *tbinfo = tdinfo->tdtable;
PGresult *res;
- char *last;
- bool called;
PQExpBuffer query = createPQExpBuffer();
/* Make sure we are in proper schema */
selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
- appendPQExpBuffer(query,
- "SELECT last_value, is_called FROM %s",
- fmtId(tbinfo->dobj.name));
+ /*
+ * On 9.5 there is special interface for dumping sequences but we only
+ * use it for one with nondefault access method because we can produce
+ * more backward compatible dump that way.
+ */
+ if (fout->remoteVersion >= 90500 &&
+ tbinfo->relam != InvalidOid &&
+ tbinfo->relam != LOCAL_SEQAM_OID)
+ {
+ appendPQExpBuffer(query,
+ "SELECT quote_literal(pg_catalog.pg_sequence_get_state(");
+ appendStringLiteralAH(query, tbinfo->dobj.name, fout);
+ appendPQExpBuffer(query, "))");
+ }
+ else
+ appendPQExpBuffer(query,
+ "SELECT last_value, is_called FROM %s",
+ fmtId(tbinfo->dobj.name));
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -15026,14 +15100,29 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
exit_nicely(1);
}
- last = PQgetvalue(res, 0, 0);
- called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
-
resetPQExpBuffer(query);
- appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
- appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
- appendPQExpBuffer(query, ", %s, %s);\n",
- last, (called ? "true" : "false"));
+
+ if (fout->remoteVersion >= 90500 &&
+ tbinfo->relam != InvalidOid &&
+ tbinfo->relam != LOCAL_SEQAM_OID)
+ {
+ char *state = PQgetvalue(res, 0, 0);
+
+ appendPQExpBufferStr(query, "SELECT pg_catalog.pg_sequence_set_state(");
+ appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+ /* The state got quote in the SELECT. */
+ appendPQExpBuffer(query, ", %s);\n", state);
+ }
+ else
+ {
+ char *last = PQgetvalue(res, 0, 0);
+ bool called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
+
+ appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
+ appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+ appendPQExpBuffer(query, ", %s, %s);\n",
+ last, (called ? "true" : "false"));
+ }
ArchiveEntry(fout, nilCatalogId, createDumpId(),
tbinfo->dobj.name,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 4c796ad..3d2b66a 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -222,6 +222,7 @@ typedef struct _tableInfo
/* these two are set only if table is a sequence owned by a column: */
Oid owning_tab; /* OID of table owning sequence */
int owning_col; /* attr # of column owning sequence */
+ int relam; /* access method (from pg_clas) */
int relpages; /* table's size in pages (from pg_class) */
bool interesting; /* true if need to collect more data */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 04d769e..a01f319 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1401,30 +1401,6 @@ describeOneTableDetails(const char *schemaname,
res = NULL;
/*
- * If it's a sequence, fetch its values and store into an array that will
- * be used later.
- */
- if (tableinfo.relkind == 'S')
- {
- printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
- /* must be separate because fmtId isn't reentrant */
- appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
-
- res = PSQLexec(buf.data);
- if (!res)
- goto error_return;
-
- seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
-
- for (i = 0; i < PQnfields(res); i++)
- seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
- seq_values[i] = NULL;
-
- PQclear(res);
- res = NULL;
- }
-
- /*
* Get column info
*
* You need to modify value of "firstvcol" which will be defined below if
@@ -1468,13 +1444,55 @@ describeOneTableDetails(const char *schemaname,
appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
+
+ /*
+ * For sequence, fetch only the common column unless verbose was specified.
+ * Note that this is change from pre9.5 versions.
+ */
+ if (tableinfo.relkind == 'S')
+ appendPQExpBufferStr(&buf, " AND attname <> 'amdata'");
+
appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
+
res = PSQLexec(buf.data);
if (!res)
goto error_return;
numrows = PQntuples(res);
+ /*
+ * If it's a sequence, fetch its values and store into an array that will
+ * be used later.
+ */
+ if (tableinfo.relkind == 'S')
+ {
+ PGresult *result;
+
+ /*
+ * Use column names from the column info query, to automatically skip
+ * unwanted columns.
+ */
+ printfPQExpBuffer(&buf, "SELECT ");
+ for (i = 0; i < numrows; i++)
+ appendPQExpBuffer(&buf, i > 0 ? ", %s" : "%s", fmtId(PQgetvalue(res, i, 0)));
+ appendPQExpBuffer(&buf, " FROM %s",
+ fmtId(schemaname));
+ /* must be separate because fmtId isn't reentrant */
+ appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+ result = PSQLexec(buf.data);
+ if (!result)
+ goto error_return;
+
+ seq_values = pg_malloc((PQnfields(result) + 1) * sizeof(*seq_values));
+
+ for (i = 0; i < PQnfields(result); i++)
+ seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
+ seq_values[i] = NULL;
+
+ PQclear(result);
+ }
+
/* Make title */
switch (tableinfo.relkind)
{
@@ -1803,6 +1821,8 @@ describeOneTableDetails(const char *schemaname,
oid);
result = PSQLexec(buf.data);
+
+ /* Same logic as above, only print result when we get one row. */
if (!result)
goto error_return;
else if (PQntuples(result) == 1)
@@ -1812,12 +1832,56 @@ describeOneTableDetails(const char *schemaname,
printTableAddFooter(&cont, buf.data);
}
+ PQclear(result);
+
+ /* Get the Access Method name for the sequence */
+ printfPQExpBuffer(&buf, "SELECT a.seqamname\n"
+ "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n"
+ "WHERE c.relam = a.oid AND c.oid = %s", oid);
+
+ result = PSQLexec(buf.data);
+
/*
* If we get no rows back, don't show anything (obviously). We should
* never get more than one row back, but if we do, just ignore it and
* don't print anything.
*/
+ if (!result)
+ goto error_return;
+ else if (PQntuples(result) == 1)
+ {
+ printfPQExpBuffer(&buf, _("Access Method: %s"),
+ PQgetvalue(result, 0, 0));
+ printTableAddFooter(&cont, buf.data);
+ }
+
PQclear(result);
+
+ if (verbose)
+ {
+ /* Get the Access Method state */
+ printfPQExpBuffer(&buf,
+ "SELECT pg_catalog.pg_sequence_get_state('%s');",
+ oid);
+
+ result = PSQLexec(buf.data);
+
+ /*
+ * If we get no rows back, don't show anything (obviously). We should
+ * never get more than one row back, but if we do, just ignore it and
+ * don't print anything.
+ */
+ if (!result)
+ goto error_return;
+ else if (PQntuples(result) == 1)
+ {
+ printfPQExpBuffer(&buf, _("Access Method State: %s"),
+ PQgetvalue(result, 0, 0));
+ printTableAddFooter(&cont, buf.data);
+ }
+
+ PQclear(result);
+ }
}
else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
tableinfo.relkind == 'f')
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index e7b6bb5..7523973 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -270,8 +270,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
- bool validate);
+extern bytea *am_reloptions(RegProcedure amoptions, Datum reloptions,
+ bool validate);
extern bytea *attribute_reloptions(Datum reloptions, bool validate);
extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
new file mode 100644
index 0000000..5040118
--- /dev/null
+++ b/src/include/access/seqam.h
@@ -0,0 +1,69 @@
+/*-------------------------------------------------------------------------
+ *
+ * seqam.h
+ * Public header file for Sequence access method.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/seqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SEQAM_H
+#define SEQAM_H
+
+#include "fmgr.h"
+
+#include "access/htup.h"
+#include "commands/sequence.h"
+#include "utils/relcache.h"
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+
+
+struct SequenceHandle;
+typedef struct SequenceHandle SequenceHandle;
+
+extern char *serial_seqam;
+
+extern HeapTuple seqam_init(Relation seqrel, HeapTuple tuple,
+ int64 restart_value, bool restart_requested,
+ bool is_init);
+extern int64 seqam_alloc(Relation seqrel, SequenceHandle *seqh,
+ int64 nrequested, int64 *last);
+extern void seqam_setval(Relation seqrel, SequenceHandle *seqh,
+ int64 new_value);
+extern ArrayType * seqam_get_state(Relation seqrel, SequenceHandle *seqh);
+extern void seqam_set_state(Relation seqrel, SequenceHandle *seqh,
+ ArrayType *statearr);
+
+extern Oid get_seqam_oid(const char *sequencename, bool missing_ok);
+
+extern void sequence_open(Oid seqrelid, SequenceHandle *seqh);
+extern void sequence_close(SequenceHandle *seqh);
+extern HeapTuple sequence_read_tuple(SequenceHandle *seqh);
+extern void sequence_swap_tuple(SequenceHandle *seqh, HeapTuple newtup);
+extern void sequence_start_update(SequenceHandle *seqh, bool do_wal);
+extern void sequence_apply_update(SequenceHandle *seqh, bool do_wal);
+extern void sequence_finish_update(SequenceHandle *seqh);
+extern void sequence_release_tuple(SequenceHandle *seqh);
+extern bool sequence_needs_wal(SequenceHandle *seqh);
+
+extern int64 sequence_increment(Relation seqrel, int64 *value, int64 incnum,
+ int64 minv, int64 maxv, int64 incby,
+ bool is_cycled, bool report_errors);
+extern void sequence_check_range(int64 value, int64 min_value,
+ int64 max_value, const char *valname);
+extern int64 sequence_get_restart_value(List *options, int64 default_value,
+ bool *found);
+
+extern Datum seqam_local_reloptions(PG_FUNCTION_ARGS);
+extern Datum seqam_local_init(PG_FUNCTION_ARGS);
+extern Datum seqam_local_alloc(PG_FUNCTION_ARGS);
+extern Datum seqam_local_setval(PG_FUNCTION_ARGS);
+extern Datum seqam_local_get_state(PG_FUNCTION_ARGS);
+extern Datum seqam_local_set_state(PG_FUNCTION_ARGS);
+
+#endif /* SEQAM_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index e6ac394..ce0ba57 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -71,6 +71,7 @@ extern Oid heap_create_with_catalog(const char *relname,
bool use_user_acl,
bool allow_system_table_mods,
bool is_internal,
+ Oid relam,
ObjectAddress *typaddress);
extern void heap_create_init_fork(Relation rel);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index a234bde..e4c634f 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -206,6 +206,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
#define RewriteRelRulenameIndexId 2693
+DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 6020, on pg_seqam using btree(seqamname name_ops));
+#define SeqAMNameIndexId 6020
+DECLARE_UNIQUE_INDEX(pg_seqam_oid_index, 6021, on pg_seqam using btree(oid oid_ops));
+#define SeqAMOidIndexId 6021
+
DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
#define SharedDependDependerIndexId 1232
DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e97e6b1..aacd906 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1833,6 +1833,10 @@ DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 20 "
DESCR("set sequence value and is_called status");
DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_));
DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 3377 ( pg_sequence_get_state PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 1009 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a sequence");
+DATA(insert OID = 3378 ( pg_sequence_set_state PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 1009" _null_ _null_ _null_ _null_ _null_ pg_sequence_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a sequence");
DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
DESCR("I/O");
@@ -5121,6 +5125,20 @@ DESCR("peek at changes from replication slot");
DATA(insert OID = 3785 ( pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
DESCR("peek at binary changes from replication slot");
+DATA(insert OID = 6023 ( seqam_local_reloptions PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 17 "2281 16" _null_ _null_ _null_ _null_ _null_ seqam_local_reloptions _null_ _null_ _null_ ));
+DESCR("Local SequenceAM options");
+DATA(insert OID = 6024 ( seqam_local_init PGNSP PGUID 12 1 0 0 0 f f f f f f s 5 0 17 "2281 2281 20 16 16" _null_ _null_ _null_ _null_ _null_ seqam_local_init _null_ _null_ _null_ ));
+DESCR("Initialize local SequenceAM sequence");
+DATA(insert OID = 6025 ( seqam_local_alloc PGNSP PGUID 12 1 0 0 0 f f f f f f v 4 0 2281 "2281 2281 20 2281" _null_ _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ ));
+DESCR("Local SequenceAM allocation");
+DATA(insert OID = 6026 ( seqam_local_setval PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 20" _null_ _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ ));
+DESCR("Set value of a local SequenceAM sequence");
+DATA(insert OID = 6027 ( seqam_local_get_state PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 1009 "2281 2281" _null_ _null_ _null_ _null_ _null_ seqam_local_get_state _null_ _null_ _null_ ));
+DESCR("Dump state of a local SequenceAM sequence");
+DATA(insert OID = 6028 ( seqam_local_set_state PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2278 "2281 2281 1009" _null_ _null_ _null_ _null_ _null_ seqam_local_set_state _null_ _null_ _null_ ));
+DESCR("Restore state of a local SequenceAM sequence");
+
+
/* event triggers */
DATA(insert OID = 3566 ( pg_event_trigger_dropped_objects PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, is_temporary, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
DESCR("list objects dropped by the current command");
diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h
new file mode 100644
index 0000000..dec234a
--- /dev/null
+++ b/src/include/catalog/pg_seqam.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_seqam.h
+ * definition of the system "sequence access method" relation (pg_seqam)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_seqam.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SEQAM_H
+#define PG_SEQAM_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_seqam definition. cpp turns this into
+ * typedef struct FormData_pg_seqam
+ * ----------------
+ */
+#define SeqAccessMethodRelationId 3286
+
+CATALOG(pg_seqam,3286)
+{
+ NameData seqamname; /* access method name */
+ regproc seqamreloptions; /* parse AM-specific options */
+ regproc seqaminit; /* sequence initialization */
+ regproc seqamalloc; /* get next allocation of range of values function */
+ regproc seqamsetval; /* set value function */
+ regproc seqamgetstate; /* dump state, used by pg_dump */
+ regproc seqamsetstate; /* restore state, used when loading pg_dump */
+} FormData_pg_seqam;
+
+/* ----------------
+ * Form_pg_seqam corresponds to a pointer to a tuple with
+ * the format of pg_seqam relation.
+ * ----------------
+ */
+typedef FormData_pg_seqam *Form_pg_seqam;
+
+/* ----------------
+ * compiler constants for pg_seqam
+ * ----------------
+ */
+#define Natts_pg_seqam 7
+#define Anum_pg_seqam_seqamname 1
+#define Anum_pg_seqam_seqamreloptions 2
+#define Anum_pg_seqam_seqaminit 3
+#define Anum_pg_seqam_seqamalloc 4
+#define Anum_pg_seqam_seqamsetval 5
+#define Anum_pg_seqam_seqamgetstate 6
+#define Anum_pg_seqam_seqamsetstate 7
+
+/* ----------------
+ * initial contents of pg_seqam
+ * ----------------
+ */
+
+DATA(insert OID = 6022 ( local seqam_local_reloptions seqam_local_init seqam_local_alloc seqam_local_setval seqam_local_get_state seqam_local_set_state));
+DESCR("local sequence access method");
+#define LOCAL_SEQAM_OID 6022
+
+#endif /* PG_SEQAM_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 44862bb..bed8d7d 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -30,9 +30,9 @@ typedef struct FormData_pg_sequence
int64 max_value;
int64 min_value;
int64 cache_value;
- int64 log_cnt;
bool is_cycled;
bool is_called;
+ bytea amdata;
} FormData_pg_sequence;
typedef FormData_pg_sequence *Form_pg_sequence;
@@ -48,12 +48,12 @@ typedef FormData_pg_sequence *Form_pg_sequence;
#define SEQ_COL_MAXVALUE 5
#define SEQ_COL_MINVALUE 6
#define SEQ_COL_CACHE 7
-#define SEQ_COL_LOG 8
-#define SEQ_COL_CYCLE 9
-#define SEQ_COL_CALLED 10
+#define SEQ_COL_CYCLE 8
+#define SEQ_COL_CALLED 9
+#define SEQ_COL_AMDATA 10
#define SEQ_COL_FIRSTCOL SEQ_COL_NAME
-#define SEQ_COL_LASTCOL SEQ_COL_CALLED
+#define SEQ_COL_LASTCOL SEQ_COL_AMDATA
/* XLOG stuff */
#define XLOG_SEQ_LOG 0x00
@@ -72,6 +72,8 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
extern Datum lastval(PG_FUNCTION_ARGS);
extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_get_state(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_set_state(PG_FUNCTION_ARGS);
extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index f269c63..cb621b1 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -23,7 +23,7 @@
extern ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
- ObjectAddress *typaddress);
+ Oid relamid, ObjectAddress *typaddress);
extern void RemoveRelations(DropStmt *drop);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 852eb4f..c55c9de 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2040,8 +2040,10 @@ typedef struct CreateSeqStmt
{
NodeTag type;
RangeVar *sequence; /* the sequence to create */
- List *options;
+ List *options; /* standard sequence options */
+ List *amoptions; /* am specific options */
Oid ownerId; /* ID of owner, or InvalidOid for default */
+ char *accessMethod; /* USING name of access method (eg. Local) */
bool if_not_exists; /* just do nothing if it already exists? */
} CreateSeqStmt;
@@ -2049,8 +2051,10 @@ typedef struct AlterSeqStmt
{
NodeTag type;
RangeVar *sequence; /* the sequence to alter */
- List *options;
+ List *options; /* standard sequence options */
+ List *amoptions; /* am specific options */
bool missing_ok; /* skip error if a role is missing? */
+ char *accessMethod; /* USING name of access method (eg. Local) */
} AlterSeqStmt;
/* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ff78b70..1ac3ef7 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -410,6 +410,7 @@ extern void GUC_check_errcode(int sqlerrcode);
*/
/* in commands/tablespace.c */
+extern bool check_serial_seqam(char **newval, void **extra, GucSource source);
extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
extern void assign_temp_tablespaces(const char *newval, void *extra);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 9e17d87..c61bc41 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
#include "catalog/pg_am.h"
#include "catalog/pg_class.h"
#include "catalog/pg_index.h"
+#include "catalog/pg_seqam.h"
#include "fmgr.h"
#include "nodes/bitmapset.h"
#include "rewrite/prs2lock.h"
@@ -65,6 +66,18 @@ typedef struct RelationAmInfo
/*
+ * Cached lookup information for the frequently used sequence access method
+ * functions, defined by the pg_seqam row associated with a sequencerelation.
+ */
+typedef struct RelationSeqAmInfo
+{
+ /* pg_seqam only */
+ FmgrInfo seqamalloc;
+ FmgrInfo seqamsetval;
+} RelationSeqAmInfo;
+
+
+/*
* Here are the contents of a relation cache entry.
*/
@@ -125,6 +138,10 @@ typedef struct RelationData
*/
bytea *rd_options; /* parsed pg_class.reloptions */
+ /* These are non-NULL only for a sequence relation */
+ Form_pg_seqam rd_seqam; /* pg_seqam tuple for sequence's AM */
+ RelationSeqAmInfo *rd_seqaminfo; /* lookup info for funcs found in pg_seqam */
+
/* These are non-NULL only for an index relation: */
Form_pg_index rd_index; /* pg_index tuple describing this index */
/* use "struct" here to avoid needing to include htup.h: */
@@ -132,7 +149,7 @@ typedef struct RelationData
Form_pg_am rd_am; /* pg_am tuple for index's AM */
/*
- * index access support info (used only for an index relation)
+ * index access support info (used only for index relations)
*
* Note: only default support procs for each opclass are cached, namely
* those with lefttype and righttype equal to the opclass's opcintype. The
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index ff9a4f2..e7a4224 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -78,6 +78,8 @@ enum SysCacheIdentifier
RELNAMENSP,
RELOID,
RULERELNAME,
+ SEQAMNAME,
+ SEQAMOID,
STATRELATTINH,
TABLESPACEOID,
TRFOID,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 52c9e77..b9308f6 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -123,6 +123,7 @@ pg_proc|t
pg_range|t
pg_rewrite|t
pg_seclabel|t
+pg_seqam|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 8783ca6..3dc5af9 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -129,10 +129,10 @@ SELECT nextval('sequence_test'::regclass);
33
(1 row)
-SELECT setval('sequence_test'::text, 99, false);
- setval
---------
- 99
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state
+-----------------------
+
(1 row)
SELECT nextval('sequence_test'::regclass);
@@ -153,10 +153,10 @@ SELECT nextval('sequence_test'::text);
33
(1 row)
-SELECT setval('sequence_test'::regclass, 99, false);
- setval
---------
- 99
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
+ pg_sequence_set_state
+-----------------------
+
(1 row)
SELECT nextval('sequence_test'::text);
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
CREATE SEQUENCE foo_seq;
ALTER TABLE foo_seq RENAME TO foo_seq_new;
SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f
+ sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | is_cycled | is_called | amdata
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | f | f | \x0000000000000000
(1 row)
SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
(1 row)
SELECT * FROM foo_seq_new;
- sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t
+ sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | is_cycled | is_called | amdata
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+-----------+-----------+--------------------
+ foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | f | t | \x1f00000000000000
(1 row)
DROP SEQUENCE foo_seq_new;
@@ -300,6 +300,13 @@ SELECT nextval('sequence_test2');
5
(1 row)
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+ERROR: local sequence does not accept any storage parameters
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+ERROR: local sequence does not accept any storage parameters
+DROP SEQUENCE myamseq;
-- Information schema
SELECT * FROM information_schema.sequences WHERE sequence_name IN
('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index ccabe9e..34faedd 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -106,9 +106,9 @@ SELECT table_name, column_name, is_updatable
ro_view19 | max_value | NO
ro_view19 | min_value | NO
ro_view19 | cache_value | NO
- ro_view19 | log_cnt | NO
ro_view19 | is_cycled | NO
ro_view19 | is_called | NO
+ ro_view19 | amdata | NO
ro_view2 | a | NO
ro_view2 | b | NO
ro_view20 | a | NO
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 0dd653d..0a54569 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -67,11 +67,11 @@ SELECT currval('sequence_test'::text);
SELECT currval('sequence_test'::regclass);
SELECT setval('sequence_test'::text, 32);
SELECT nextval('sequence_test'::regclass);
-SELECT setval('sequence_test'::text, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::text, '{last_value, 99, is_called, false}');
SELECT nextval('sequence_test'::regclass);
SELECT setval('sequence_test'::regclass, 32);
SELECT nextval('sequence_test'::text);
-SELECT setval('sequence_test'::regclass, 99, false);
+SELECT pg_sequence_set_state('sequence_test'::regclass, '{last_value, 99, is_called, false}');
SELECT nextval('sequence_test'::text);
DISCARD SEQUENCES;
SELECT currval('sequence_test'::regclass);
@@ -138,6 +138,12 @@ SELECT nextval('sequence_test2');
SELECT nextval('sequence_test2');
SELECT nextval('sequence_test2');
+-- Sequence Acess Method
+CREATE SEQUENCE myamseq USING local WITH (foo = 'bar');
+CREATE SEQUENCE myamseq USING local;
+ALTER SEQUENCE myamseq SET (foo = 'baz');
+DROP SEQUENCE myamseq;
+
-- Information schema
SELECT * FROM information_schema.sequences WHERE sequence_name IN
('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
--
1.9.1