Unlogged sequences
authorPeter Eisentraut <peter@eisentraut.org>
Thu, 7 Apr 2022 14:13:23 +0000 (16:13 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Thu, 7 Apr 2022 14:18:00 +0000 (16:18 +0200)
Add support for unlogged sequences.  Unlike for unlogged tables, this
is not a performance feature.  It allows sequences associated with
unlogged tables to be excluded from replication.

A new subcommand ALTER SEQUENCE ... SET LOGGED/UNLOGGED is added.

An identity/serial sequence now automatically gets and follows the
persistence level (logged/unlogged) of its owning table.  (The
sequences owned by temporary tables were already temporary through the
separate mechanism in RangeVarAdjustRelationPersistence().)  But you
can still change the persistence of an owned sequence separately.
Also, pg_dump and pg_upgrade preserve the persistence of existing
sequences.

Discussion: https://www.postgresql.org/message-id/flat/04e12818-2f98-257c-b926-2845d74ed04f%402ndquadrant.com

16 files changed:
doc/src/sgml/ref/alter_sequence.sgml
doc/src/sgml/ref/alter_table.sgml
doc/src/sgml/ref/create_sequence.sgml
doc/src/sgml/ref/create_table.sgml
doc/src/sgml/ref/pg_dump.sgml
src/backend/commands/sequence.c
src/backend/commands/tablecmds.c
src/backend/parser/parse_utilcmd.c
src/bin/pg_dump/pg_dump.c
src/bin/psql/describe.c
src/bin/psql/tab-complete.c
src/include/commands/sequence.h
src/test/recovery/t/014_unlogged_reinit.pl
src/test/regress/expected/alter_table.out
src/test/regress/expected/sequence.out
src/test/regress/sql/sequence.sql

index 3cd9ece49f2234865151c8267cc9b7ce5c7c6eab..148085d4f20797c0ea01c7d6c02f890a3351e5d7 100644 (file)
@@ -31,6 +31,7 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
     [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ]
     [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ]
+ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET { LOGGED | UNLOGGED }
 ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="parameter">new_owner</replaceable> | CURRENT_ROLE | 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>
@@ -237,6 +238,17 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><literal>SET { LOGGED | UNLOGGED }</literal></term>
+      <listitem>
+       <para>
+        This form changes the sequence from unlogged to logged or vice-versa
+        (see <xref linkend="sql-createsequence"/>).  It cannot be applied to a
+        temporary sequence.
+       </para>
+      </listitem>
+     </varlistentry>
+
    <varlistentry>
     <term><literal>OWNED BY</literal> <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable></term>
     <term><literal>OWNED BY NONE</literal></term>
index e610cbbc0ecb4781c35d1c544eabe4e5fedd556e..c2a458eb5dbe5792cee7e55552659b2f293ac01f 100644 (file)
@@ -753,6 +753,12 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
       (see <xref linkend="sql-createtable-unlogged"/>).  It cannot be applied
       to a temporary table.
      </para>
+
+     <para>
+      This also changes the persistence of any sequences linked to the table
+      (for identity or serial columns).  However, it is also possible to
+      change the persistence of such sequences separately.
+     </para>
     </listitem>
    </varlistentry>
 
index 20bdbc002fa0890c5483c6d754f1f63fee1e4ab3..a84aa5bf56a27514f9bc8fe9a4ba3eb4bf52ffb0 100644 (file)
@@ -21,7 +21,7 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable>
+CREATE [ { TEMPORARY | TEMP } | UNLOGGED ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable>
     [ AS <replaceable class="parameter">data_type</replaceable> ]
     [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ]
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
@@ -92,6 +92,27 @@ SELECT * FROM <replaceable>name</replaceable>;
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>UNLOGGED</literal></term>
+    <listitem>
+     <para>
+      If specified, the sequence is created as an unlogged sequence.  Changes
+      to unlogged sequences are not written to the write-ahead log.  They are
+      not crash-safe: an unlogged sequence is automatically reset to its
+      initial state after a crash or unclean shutdown.  Unlogged sequences are
+      also not replicated to standby servers.
+     </para>
+
+     <para>
+      Unlike unlogged tables, unlogged sequences do not offer a significant
+      performance advantage.  This option is mainly intended for sequences
+      associated with unlogged tables via identity columns or serial columns.
+      In those cases, it usually wouldn't make sense to have the sequence
+      WAL-logged and replicated but not its associated table.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>IF NOT EXISTS</literal></term>
     <listitem>
index 7e4ef312c0ea965cd7b4813fbfa3c89c4131e313..6c9918b0a1eb283b0bc5aa3522e71a67f5ba7648 100644 (file)
@@ -215,6 +215,11 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
       Any indexes created on an unlogged table are automatically unlogged as
       well.
      </para>
+
+     <para>
+      If this is specified, any sequences created together with the unlogged
+      table (for identity or serial columns) are also created as unlogged.
+     </para>
     </listitem>
    </varlistentry>
 
index 2f0042fd968dfd5ab86dcb578b5b0154f0547af3..723b2a1a66add2d42d1eb3a814bbf882a091d744 100644 (file)
@@ -981,9 +981,10 @@ PostgreSQL documentation
       <term><option>--no-unlogged-table-data</option></term>
       <listitem>
        <para>
-        Do not dump the contents of unlogged tables.  This option has no
-        effect on whether or not the table definitions (schema) are dumped;
-        it only suppresses dumping the table data. Data in unlogged tables
+        Do not dump the contents of unlogged tables and sequences.  This
+        option has no effect on whether or not the table and sequence
+        definitions (schema) are dumped; it only suppresses dumping the table
+        and sequence data. Data in unlogged tables and sequences
         is always excluded when dumping from a standby server.
        </para>
       </listitem>
index 717bb0b2aa9d39fb58ac7d92d00b31f9cf98d9f1..47f62c28d4252118b74ba76370a99fcf4144f9c1 100644 (file)
@@ -30,6 +30,7 @@
 #include "catalog/objectaccess.h"
 #include "catalog/pg_sequence.h"
 #include "catalog/pg_type.h"
+#include "catalog/storage_xlog.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
 #include "commands/tablecmds.h"
@@ -95,6 +96,7 @@ static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 static SeqTableData *last_used_seq = NULL;
 
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
+static void fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum);
 static Relation lock_and_open_sequence(SeqTable seq);
 static void create_seq_hashtable(void);
 static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
@@ -133,12 +135,6 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
    bool        pgs_nulls[Natts_pg_sequence];
    int         i;
 
-   /* Unlogged sequences are not implemented -- not clear if useful. */
-   if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("unlogged sequences are not supported")));
-
    /*
     * If if_not_exists was given and a relation with the same name already
     * exists, bail out. (Note: we needn't check this when not if_not_exists,
@@ -492,9 +488,33 @@ SetSequence(Oid seq_relid, bool transactional, int64 last_value, int64 log_cnt,
 
 /*
  * Initialize a sequence's relation with the specified tuple as content
+ *
+ * This handles unlogged sequences by writing to both the main and the init
+ * fork as necessary.
  */
 static void
 fill_seq_with_data(Relation rel, HeapTuple tuple)
+{
+   fill_seq_fork_with_data(rel, tuple, MAIN_FORKNUM);
+
+   if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
+   {
+       SMgrRelation srel;
+
+       srel = smgropen(rel->rd_node, InvalidBackendId);
+       smgrcreate(srel, INIT_FORKNUM, false);
+       log_smgrcreate(&rel->rd_node, INIT_FORKNUM);
+       fill_seq_fork_with_data(rel, tuple, INIT_FORKNUM);
+       FlushRelationBuffers(rel);
+       smgrclose(srel);
+   }
+}
+
+/*
+ * Initialize a sequence's relation fork with the specified tuple as content
+ */
+static void
+fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
 {
    Buffer      buf;
    Page        page;
@@ -503,7 +523,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 
    /* Initialize first page of relation with special magic number */
 
-   buf = ReadBuffer(rel, P_NEW);
+   buf = ReadBufferExtended(rel, forkNum, P_NEW, RBM_NORMAL, NULL);
    Assert(BufferGetBlockNumber(buf) == 0);
 
    page = BufferGetPage(buf);
@@ -549,7 +569,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
        elog(ERROR, "failed to add sequence tuple to page");
 
    /* XLOG stuff */
-   if (RelationNeedsWAL(rel))
+   if (RelationNeedsWAL(rel) || forkNum == INIT_FORKNUM)
    {
        xl_seq_rec  xlrec;
        XLogRecPtr  recptr;
@@ -682,6 +702,28 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
    return address;
 }
 
+void
+SequenceChangePersistence(Oid relid, char newrelpersistence)
+{
+   SeqTable    elm;
+   Relation    seqrel;
+   Buffer      buf;
+   HeapTupleData seqdatatuple;
+
+   init_sequence(relid, &elm, &seqrel);
+
+   /* check the comment above nextval_internal()'s equivalent call. */
+   if (RelationNeedsWAL(seqrel))
+       GetTopTransactionId();
+
+   (void) read_seq_tuple(seqrel, &buf, &seqdatatuple);
+   RelationSetNewRelfilenode(seqrel, newrelpersistence);
+   fill_seq_with_data(seqrel, &seqdatatuple);
+   UnlockReleaseBuffer(buf);
+
+   relation_close(seqrel, NoLock);
+}
+
 void
 DeleteSequenceTuple(Oid relid)
 {
index a241b444975474db7132e148308f973c9a5d3b81..4dd545cdd20cff9cc2962ce81c8ba524201bd1e1 100644 (file)
@@ -319,6 +319,7 @@ struct DropRelationCallbackState
 #define        ATT_COMPOSITE_TYPE      0x0010
 #define        ATT_FOREIGN_TABLE       0x0020
 #define        ATT_PARTITIONED_INDEX   0x0040
+#define        ATT_SEQUENCE            0x0080
 
 /*
  * ForeignTruncateInfo
@@ -4660,7 +4661,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
            pass = AT_PASS_MISC;
            break;
        case AT_SetLogged:      /* SET LOGGED */
-           ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
+           ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
            if (tab->chgPersistence)
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -4675,7 +4676,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
            pass = AT_PASS_MISC;
            break;
        case AT_SetUnLogged:    /* SET UNLOGGED */
-           ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
+           ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
            if (tab->chgPersistence)
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -5425,7 +5426,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
         * and assigns a new relfilenode, we automatically create or drop an
         * init fork for the relation as appropriate.
         */
-       if (tab->rewrite > 0)
+       if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
        {
            /* Build a temporary relation and copy data */
            Relation    OldHeap;
@@ -5546,6 +5547,11 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
 
            InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
        }
+       else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
+       {
+           if (tab->chgPersistence)
+               SequenceChangePersistence(tab->relid, tab->newrelpersistence);
+       }
        else
        {
            /*
@@ -5564,6 +5570,23 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
            if (tab->newTableSpace)
                ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
        }
+
+       /*
+        * Also change persistence of owned sequences, so that it matches the
+        * table persistence.
+        */
+       if (tab->chgPersistence)
+       {
+           List       *seqlist = getOwnedSequences(tab->relid);
+           ListCell   *lc;
+
+           foreach(lc, seqlist)
+           {
+               Oid         seq_relid = lfirst_oid(lc);
+
+               SequenceChangePersistence(seq_relid, tab->newrelpersistence);
+           }
+       }
    }
 
    /*
@@ -6224,6 +6247,9 @@ ATSimplePermissions(AlterTableType cmdtype, 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;
index cd946c7692127eb0b57ae96b8226c6ff456087dc..2826559d09b746bd3d7c02dfc68d987d0137550d 100644 (file)
@@ -445,6 +445,7 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
    seqstmt = makeNode(CreateSeqStmt);
    seqstmt->for_identity = for_identity;
    seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
+   seqstmt->sequence->relpersistence = cxt->relation->relpersistence;
    seqstmt->options = seqoptions;
 
    /*
index 535b1601655f4b533f77b9b94f9bec8fd7ca4806..3c2201a725f645dab71837014a822547c50e44c5 100644 (file)
@@ -16570,6 +16570,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
    PQExpBuffer query = createPQExpBuffer();
    PQExpBuffer delqry = createPQExpBuffer();
    char       *qseqname;
+   TableInfo  *owning_tab = NULL;
 
    qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
@@ -16678,7 +16679,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 
    if (tbinfo->is_identity_sequence)
    {
-       TableInfo  *owning_tab = findTableByOid(tbinfo->owning_tab);
+       owning_tab = findTableByOid(tbinfo->owning_tab);
 
        appendPQExpBuffer(query,
                          "ALTER TABLE %s ",
@@ -16696,7 +16697,9 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
    else
    {
        appendPQExpBuffer(query,
-                         "CREATE SEQUENCE %s\n",
+                         "CREATE %sSEQUENCE %s\n",
+                         tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
+                         "UNLOGGED " : "",
                          fmtQualifiedDumpable(tbinfo));
 
        if (strcmp(seqtype, "bigint") != 0)
@@ -16722,7 +16725,15 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
                      cache, (cycled ? "\n    CYCLE" : ""));
 
    if (tbinfo->is_identity_sequence)
+   {
        appendPQExpBufferStr(query, "\n);\n");
+       if (tbinfo->relpersistence != owning_tab->relpersistence)
+           appendPQExpBuffer(query,
+                             "ALTER SEQUENCE %s SET %s;\n",
+                             fmtQualifiedDumpable(tbinfo),
+                             tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
+                             "UNLOGGED" : "LOGGED");
+   }
    else
        appendPQExpBufferStr(query, ";\n");
 
index 4dddf087893a2b12b25e2816ad48ec58a350fab6..73bbbe2eb404c2b18f99c7253859db4d83aa5f8b 100644 (file)
@@ -1681,8 +1681,12 @@ describeOneTableDetails(const char *schemaname,
        printTableInit(&cont, &myopt, title.data, 7, numrows);
        printTableInitialized = true;
 
-       printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
-                         schemaname, relationname);
+       if (tableinfo.relpersistence == 'u')
+           printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
+                             schemaname, relationname);
+       else
+           printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
+                             schemaname, relationname);
 
        printTableAddHeader(&cont, gettext_noop("Type"), true, 'l');
        printTableAddHeader(&cont, gettext_noop("Start"), true, 'r');
index 32d0b4855f51e0d75c20df0644adbfa34eff2871..025d3f71a114a5a642692b10b79eaf95a4308141 100644 (file)
@@ -2116,7 +2116,7 @@ psql_completion(const char *text, int start, int end)
    /* ALTER SEQUENCE <name> */
    else if (Matches("ALTER", "SEQUENCE", MatchAny))
        COMPLETE_WITH("AS", "INCREMENT", "MINVALUE", "MAXVALUE", "RESTART",
-                     "NO", "CACHE", "CYCLE", "SET SCHEMA", "OWNED BY",
+                     "NO", "CACHE", "CYCLE", "SET", "OWNED BY",
                      "OWNER TO", "RENAME TO");
    /* ALTER SEQUENCE <name> AS */
    else if (TailMatches("ALTER", "SEQUENCE", MatchAny, "AS"))
@@ -2124,6 +2124,9 @@ psql_completion(const char *text, int start, int end)
    /* ALTER SEQUENCE <name> NO */
    else if (Matches("ALTER", "SEQUENCE", MatchAny, "NO"))
        COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
+   /* ALTER SEQUENCE <name> SET */
+   else if (Matches("ALTER", "SEQUENCE", MatchAny, "SET"))
+       COMPLETE_WITH("SCHEMA", "LOGGED", "UNLOGGED");
    /* ALTER SERVER <name> */
    else if (Matches("ALTER", "SERVER", MatchAny))
        COMPLETE_WITH("VERSION", "OPTIONS", "OWNER TO", "RENAME TO");
index 5bab90db8e0159bc657697dea1c0b148bbb230ec..f2381982d5d4edb641c1e2aede52888ecd431c35 100644 (file)
@@ -58,6 +58,7 @@ extern List *sequence_options(Oid relid);
 
 extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
+extern void SequenceChangePersistence(Oid relid, char newrelpersistence);
 extern void DeleteSequenceTuple(Oid relid);
 extern void ResetSequence(Oid seq_relid);
 extern void SetSequence(Oid seq_relid, bool transactional, int64 last_value, int64 log_cnt, bool is_called);
index f3199fbd2e1a41f402460face8eeeb372462967e..0dca3f69fe3d3aadfc22605851a302c6408e271f 100644 (file)
@@ -18,16 +18,27 @@ $node->init;
 $node->start;
 my $pgdata = $node->data_dir;
 
-# Create an unlogged table to test that forks other than init are not
-# copied.
+# Create an unlogged table and an unlogged sequence to test that forks
+# other than init are not copied.
 $node->safe_psql('postgres', 'CREATE UNLOGGED TABLE base_unlogged (id int)');
+$node->safe_psql('postgres', 'CREATE UNLOGGED SEQUENCE seq_unlogged');
 
 my $baseUnloggedPath = $node->safe_psql('postgres',
    q{select pg_relation_filepath('base_unlogged')});
+my $seqUnloggedPath = $node->safe_psql('postgres',
+   q{select pg_relation_filepath('seq_unlogged')});
 
 # Test that main and init forks exist.
-ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork in base exists');
-ok(-f "$pgdata/$baseUnloggedPath",        'main fork in base exists');
+ok(-f "$pgdata/${baseUnloggedPath}_init", 'table init fork exists');
+ok(-f "$pgdata/$baseUnloggedPath",        'table main fork exists');
+ok(-f "$pgdata/${seqUnloggedPath}_init",  'sequence init fork exists');
+ok(-f "$pgdata/$seqUnloggedPath",         'sequence main fork exists');
+
+# Test the sequence
+is($node->safe_psql('postgres', "SELECT nextval('seq_unlogged')"),
+   1, 'sequence nextval');
+is($node->safe_psql('postgres', "SELECT nextval('seq_unlogged')"),
+   2, 'sequence nextval again');
 
 # Create an unlogged table in a tablespace.
 
@@ -44,6 +55,19 @@ my $ts1UnloggedPath = $node->safe_psql('postgres',
 ok(-f "$pgdata/${ts1UnloggedPath}_init", 'init fork in tablespace exists');
 ok(-f "$pgdata/$ts1UnloggedPath",        'main fork in tablespace exists');
 
+# Create more unlogged sequences for testing.
+$node->safe_psql('postgres', 'CREATE UNLOGGED SEQUENCE seq_unlogged2');
+# This rewrites the sequence relation in AlterSequence().
+$node->safe_psql('postgres', 'ALTER SEQUENCE seq_unlogged2 INCREMENT 2');
+$node->safe_psql('postgres', "SELECT nextval('seq_unlogged2')");
+
+$node->safe_psql('postgres',
+   'CREATE UNLOGGED TABLE tab_seq_unlogged3 (a int GENERATED ALWAYS AS IDENTITY)'
+);
+# This rewrites the sequence relation in ResetSequence().
+$node->safe_psql('postgres', 'TRUNCATE tab_seq_unlogged3 RESTART IDENTITY');
+$node->safe_psql('postgres', 'INSERT INTO tab_seq_unlogged3 DEFAULT VALUES');
+
 # Crash the postmaster.
 $node->stop('immediate');
 
@@ -54,6 +78,8 @@ append_to_file("$pgdata/${baseUnloggedPath}_fsm", 'TEST_FSM');
 # Remove main fork to test that it is recopied from init.
 unlink("$pgdata/${baseUnloggedPath}")
   or BAIL_OUT("could not remove \"${baseUnloggedPath}\": $!");
+unlink("$pgdata/${seqUnloggedPath}")
+  or BAIL_OUT("could not remove \"${seqUnloggedPath}\": $!");
 
 # the same for the tablespace
 append_to_file("$pgdata/${ts1UnloggedPath}_vm",  'TEST_VM');
@@ -64,13 +90,25 @@ unlink("$pgdata/${ts1UnloggedPath}")
 $node->start;
 
 # check unlogged table in base
-ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork in base still exists');
-ok(-f "$pgdata/$baseUnloggedPath", 'main fork in base recreated at startup');
+ok( -f "$pgdata/${baseUnloggedPath}_init",
+   'table init fork in base still exists');
+ok(-f "$pgdata/$baseUnloggedPath",
+   'table main fork in base recreated at startup');
 ok(!-f "$pgdata/${baseUnloggedPath}_vm",
    'vm fork in base removed at startup');
 ok( !-f "$pgdata/${baseUnloggedPath}_fsm",
    'fsm fork in base removed at startup');
 
+# check unlogged sequence
+ok(-f "$pgdata/${seqUnloggedPath}_init", 'sequence init fork still exists');
+ok(-f "$pgdata/$seqUnloggedPath", 'sequence main fork recreated at startup');
+
+# Test the sequence after restart
+is($node->safe_psql('postgres', "SELECT nextval('seq_unlogged')"),
+   1, 'sequence nextval after restart');
+is($node->safe_psql('postgres', "SELECT nextval('seq_unlogged')"),
+   2, 'sequence nextval after restart again');
+
 # check unlogged table in tablespace
 ok( -f "$pgdata/${ts1UnloggedPath}_init",
    'init fork still exists in tablespace');
@@ -81,4 +119,15 @@ ok( !-f "$pgdata/${ts1UnloggedPath}_vm",
 ok( !-f "$pgdata/${ts1UnloggedPath}_fsm",
    'fsm fork in tablespace removed at startup');
 
+# Test other sequences
+is($node->safe_psql('postgres', "SELECT nextval('seq_unlogged2')"),
+   1, 'altered sequence nextval after restart');
+is($node->safe_psql('postgres', "SELECT nextval('seq_unlogged2')"),
+   3, 'altered sequence nextval after restart again');
+
+$node->safe_psql('postgres',
+   "INSERT INTO tab_seq_unlogged3 VALUES (DEFAULT), (DEFAULT)");
+is($node->safe_psql('postgres', "SELECT * FROM tab_seq_unlogged3"),
+   "1\n2", 'reset sequence nextval after restart');
+
 done_testing();
index 07473dd660006c4df9a845bfbf0cf142eaf74dc7..5ede56d9b5547cb5ede13a6ce61444aaa18adfb8 100644 (file)
@@ -3461,7 +3461,7 @@ ORDER BY relname;
  unlogged1             | r       | u
  unlogged1 toast index | i       | u
  unlogged1 toast table | t       | u
- unlogged1_f1_seq      | S       | p
+ unlogged1_f1_seq      | S       | u
  unlogged1_pkey        | i       | u
 (5 rows)
 
@@ -3528,7 +3528,7 @@ ORDER BY relname;
  logged1             | r       | u
  logged1 toast index | i       | u
  logged1 toast table | t       | u
- logged1_f1_seq      | S       | p
+ logged1_f1_seq      | S       | u
  logged1_pkey        | i       | u
 (5 rows)
 
index 71c2b0f1dff42ab96854d3c0675e7661d081d2c9..7cb2f7cc02b8a596861911bb956dfb0088004b55 100644 (file)
@@ -2,8 +2,6 @@
 -- CREATE SEQUENCE
 --
 -- various error cases
-CREATE UNLOGGED SEQUENCE sequence_testx;
-ERROR:  unlogged sequences are not supported
 CREATE SEQUENCE sequence_testx INCREMENT BY 0;
 ERROR:  INCREMENT must not be zero
 CREATE SEQUENCE sequence_testx INCREMENT BY -1 MINVALUE 20;
@@ -600,6 +598,24 @@ DROP SEQUENCE seq2;
 -- should fail
 SELECT lastval();
 ERROR:  lastval is not yet defined in this session
+-- unlogged sequences
+-- (more tests in src/test/recovery/)
+CREATE UNLOGGED SEQUENCE sequence_test_unlogged;
+ALTER SEQUENCE sequence_test_unlogged SET LOGGED;
+\d sequence_test_unlogged
+                   Sequence "public.sequence_test_unlogged"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+
+ALTER SEQUENCE sequence_test_unlogged SET UNLOGGED;
+\d sequence_test_unlogged
+              Unlogged sequence "public.sequence_test_unlogged"
+  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache 
+--------+-------+---------+---------------------+-----------+---------+-------
+ bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
+
+DROP SEQUENCE sequence_test_unlogged;
 -- Test sequences in read-only transactions
 CREATE TEMPORARY SEQUENCE sequence_test_temp1;
 START TRANSACTION READ ONLY;
index 7928ee23ee8095ef57eda39883221996d59da1e3..674f5f1f668189dbeddaede21e8f269c799d552b 100644 (file)
@@ -3,7 +3,6 @@
 --
 
 -- various error cases
-CREATE UNLOGGED SEQUENCE sequence_testx;
 CREATE SEQUENCE sequence_testx INCREMENT BY 0;
 CREATE SEQUENCE sequence_testx INCREMENT BY -1 MINVALUE 20;
 CREATE SEQUENCE sequence_testx INCREMENT BY 1 MAXVALUE -20;
@@ -272,6 +271,15 @@ DROP SEQUENCE seq2;
 -- should fail
 SELECT lastval();
 
+-- unlogged sequences
+-- (more tests in src/test/recovery/)
+CREATE UNLOGGED SEQUENCE sequence_test_unlogged;
+ALTER SEQUENCE sequence_test_unlogged SET LOGGED;
+\d sequence_test_unlogged
+ALTER SEQUENCE sequence_test_unlogged SET UNLOGGED;
+\d sequence_test_unlogged
+DROP SEQUENCE sequence_test_unlogged;
+
 -- Test sequences in read-only transactions
 CREATE TEMPORARY SEQUENCE sequence_test_temp1;
 START TRANSACTION READ ONLY;