From 0935f356e7024e7351da8a4446f933e73d27cb8a Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Fri, 3 May 2019 20:55:21 +0200 Subject: [PATCH v1 3/4] Unlogged sequences --- doc/src/sgml/ref/create_sequence.sgml | 23 ++++++++++- src/backend/commands/sequence.c | 44 +++++++++++++++++++--- src/test/recovery/t/014_unlogged_reinit.pl | 33 ++++++++++++---- src/test/regress/expected/sequence.out | 5 ++- src/test/regress/sql/sequence.sql | 5 ++- 5 files changed, 93 insertions(+), 17 deletions(-) diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 3e0d339c85..6d06c6eaeb 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,7 @@ -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] name +CREATE [ { TEMPORARY | TEMP } | UNLOGGED ] SEQUENCE [ IF NOT EXISTS ] name [ AS data_type ] [ INCREMENT [ BY ] increment ] [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ] @@ -91,6 +91,27 @@ Parameters + + UNLOGGED + + + 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. + + + + 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. + + + + IF NOT EXISTS diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0960b339ca..186ddecba5 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -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" @@ -133,12 +134,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, @@ -228,6 +223,43 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) if (owned_by) process_owned_by(rel, owned_by, seq->for_identity); + /* + * create init fork for unlogged sequences + * + * The logic follows that of RelationCreateStorage() and + * RelationCopyStorage(). + */ + if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED) + { + SMgrRelation srel; + PGAlignedBlock buf; + Page page = (Page) buf.data; + + FlushRelationBuffers(rel); + + srel = smgropen(rel->rd_node, InvalidBackendId); + smgrcreate(srel, INIT_FORKNUM, false); + log_smgrcreate(&rel->rd_node, INIT_FORKNUM); + + Assert(smgrnblocks(srel, MAIN_FORKNUM) == 1); + + smgrread(srel, MAIN_FORKNUM, 0, buf.data); + + if (!PageIsVerified(page, 0)) + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg("invalid page in block %u of relation %s", + 0, + relpathbackend(srel->smgr_rnode.node, + srel->smgr_rnode.backend, + MAIN_FORKNUM)))); + + log_newpage(&srel->smgr_rnode.node, INIT_FORKNUM, 0, page, false); + PageSetChecksumInplace(page, 0); + smgrextend(srel, INIT_FORKNUM, 0, buf.data, false); + smgrclose(srel); + } + table_close(rel, NoLock); /* fill in pg_sequence */ diff --git a/src/test/recovery/t/014_unlogged_reinit.pl b/src/test/recovery/t/014_unlogged_reinit.pl index 103c0a2b91..57f502d78a 100644 --- a/src/test/recovery/t/014_unlogged_reinit.pl +++ b/src/test/recovery/t/014_unlogged_reinit.pl @@ -7,7 +7,7 @@ use warnings; use PostgresNode; use TestLib; -use Test::More tests => 12; +use Test::More tests => 20; my $node = get_new_node('main'); @@ -15,16 +15,25 @@ $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. @@ -53,6 +62,8 @@ # 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'); @@ -63,13 +74,21 @@ $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'); diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 75eb5015cf..c35ffb9f16 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -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; @@ -599,6 +597,9 @@ 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 SEQUENCE sequence_test_unlogged; -- Test sequences in read-only transactions CREATE TEMPORARY SEQUENCE sequence_test_temp1; START TRANSACTION READ ONLY; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 7928ee23ee..db04f308fc 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -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,10 @@ CREATE SEQUENCE seq2; -- should fail SELECT lastval(); +-- unlogged sequences +-- (more tests in src/test/recovery/) +CREATE SEQUENCE sequence_test_unlogged; + -- Test sequences in read-only transactions CREATE TEMPORARY SEQUENCE sequence_test_temp1; START TRANSACTION READ ONLY; -- 2.22.0