From 42584b9f0b2d34c6ac33fe9a978c22b37231e779 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Thu, 24 Mar 2022 13:54:15 +0100 Subject: [PATCH v4] Unlogged sequences 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. An identity/serial sequence is automatically made unlogged when its owning table is. (See also discussion in bug #15631.) A new subcommand ALTER SEQUENCE ... SET LOGGED/UNLOGGED is added. Also, identity/serial sequences automatically follow persistence changes of their associated tables. Discussion: https://www.postgresql.org/message-id/flat/04e12818-2f98-257c-b926-2845d74ed04f%402ndquadrant.com --- doc/src/sgml/ref/alter_sequence.sgml | 12 ++ doc/src/sgml/ref/create_sequence.sgml | 23 +++- doc/src/sgml/ref/pg_dump.sgml | 6 +- src/backend/commands/sequence.c | 58 ++++++++-- src/backend/commands/tablecmds.c | 32 +++++- src/backend/parser/parse_utilcmd.c | 1 + src/bin/pg_dump/pg_dump.c | 4 +- src/bin/psql/describe.c | 8 +- src/bin/psql/tab-complete.c | 5 +- src/include/commands/sequence.h | 1 + src/test/recovery/t/014_unlogged_reinit.pl | 49 +++++++- src/test/regress/expected/alter_table.out | 125 +++++++++++++-------- src/test/regress/expected/sequence.out | 20 +++- src/test/regress/sql/alter_table.sql | 37 +++--- src/test/regress/sql/sequence.sql | 10 +- 15 files changed, 297 insertions(+), 94 deletions(-) diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3cd9ece49f..148085d4f2 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -31,6 +31,7 @@ [ RESTART [ [ WITH ] restart ] ] [ CACHE cache ] [ [ NO ] CYCLE ] [ OWNED BY { table_name.column_name | NONE } ] +ALTER SEQUENCE [ IF EXISTS ] name SET { LOGGED | UNLOGGED } ALTER SEQUENCE [ IF EXISTS ] name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER } ALTER SEQUENCE [ IF EXISTS ] name RENAME TO new_name ALTER SEQUENCE [ IF EXISTS ] name SET SCHEMA new_schema @@ -237,6 +238,17 @@ Parameters + + SET { LOGGED | UNLOGGED } + + + This form changes the sequence from unlogged to logged or vice-versa + (see ). It cannot be applied to a + temporary sequence. + + + + OWNED BY table_name.column_name OWNED BY NONE diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 20bdbc002f..a84aa5bf56 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 ] @@ -92,6 +92,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/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index 2f0042fd96..95ccc1561a 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -981,9 +981,9 @@ Options - 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. diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index c13cada3bf..698924cbfd 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" @@ -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, @@ -338,9 +334,33 @@ ResetSequence(Oid seq_relid) /* * 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; @@ -349,7 +369,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); @@ -395,7 +415,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; @@ -528,6 +548,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) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 80faae985e..ab63f381d5 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -318,6 +318,7 @@ struct DropRelationCallbackState #define ATT_COMPOSITE_TYPE 0x0010 #define ATT_FOREIGN_TABLE 0x0020 #define ATT_PARTITIONED_INDEX 0x0040 +#define ATT_SEQUENCE 0x0080 /* * ForeignTruncateInfo @@ -4659,7 +4660,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), @@ -4674,7 +4675,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), @@ -5424,7 +5425,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; @@ -5545,6 +5546,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 { /* @@ -5563,6 +5569,23 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, if (tab->newTableSpace) ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode); } + + /* + * Also change persistence of owned sequences. + */ + if (tab->chgPersistence) + { + List *seqlist = getOwnedSequences(tab->relid); + ListCell *lc; + + foreach(lc, seqlist) + { + Oid seq_relid = lfirst_oid(lc); + + SequenceChangePersistence(seq_relid, tab->newrelpersistence); + } + } + } /* @@ -6223,6 +6246,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; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index cd946c7692..2826559d09 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -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; /* diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index e5816c4cce..a19480879e 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16614,7 +16614,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) diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 714097cad1..ccff620e2d 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -1732,8 +1732,12 @@ describeOneTableDetails(const char *schemaname, } PQclear(result); - 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); myopt.footers = footers; myopt.topt.default_footer = false; diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 5c064595a9..416215d21f 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -2100,7 +2100,7 @@ psql_completion(const char *text, int start, int end) /* ALTER SEQUENCE */ 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 AS */ else if (TailMatches("ALTER", "SEQUENCE", MatchAny, "AS")) @@ -2108,6 +2108,9 @@ psql_completion(const char *text, int start, int end) /* ALTER SEQUENCE NO */ else if (Matches("ALTER", "SEQUENCE", MatchAny, "NO")) COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE"); + /* ALTER SEQUENCE SET */ + else if (Matches("ALTER", "SEQUENCE", MatchAny, "SET")) + COMPLETE_WITH("SCHEMA", "LOGGED", "UNLOGGED"); /* ALTER SERVER */ else if (Matches("ALTER", "SERVER", MatchAny)) COMPLETE_WITH("VERSION", "OPTIONS", "OWNER TO", "RENAME TO"); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 9fecc41954..c0e0603e61 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -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 ResetSequenceCaches(void); diff --git a/src/test/recovery/t/014_unlogged_reinit.pl b/src/test/recovery/t/014_unlogged_reinit.pl index f3199fbd2e..573197738b 100644 --- a/src/test/recovery/t/014_unlogged_reinit.pl +++ b/src/test/recovery/t/014_unlogged_reinit.pl @@ -18,16 +18,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. @@ -44,6 +53,17 @@ 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 +74,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'); @@ -64,13 +86,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'); @@ -81,4 +111,11 @@ 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(); diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index aabc564e2c..3c0c29060d 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -3448,89 +3448,116 @@ ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey; ALTER TABLE old_system_table DROP COLUMN othercol; DROP TABLE old_system_table; -- set logged -CREATE UNLOGGED TABLE unlogged1(f1 SERIAL PRIMARY KEY, f2 TEXT); +CREATE UNLOGGED TABLE unlogged1(f1 INTEGER PRIMARY KEY, f2 TEXT); +CREATE UNLOGGED TABLE unlogged2(f1 INTEGER PRIMARY KEY, f2 INTEGER REFERENCES unlogged1); -- foreign key +CREATE UNLOGGED TABLE unlogged3(f1 INTEGER PRIMARY KEY, f2 INTEGER REFERENCES unlogged3); -- self-referencing foreign key +CREATE UNLOGGED TABLE unlogged4(f1 SERIAL PRIMARY KEY); -- sequence -- check relpersistence of an unlogged table -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' +SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged[1-4]' UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' +SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged[1-4]' UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' +SELECT r.relname || ' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged[1-4]' ORDER BY relname; - relname | relkind | relpersistence -------------------+---------+---------------- - toast index | i | u - toast table | t | u - unlogged1 | r | u - unlogged1_f1_seq | S | p - unlogged1_pkey | i | u -(5 rows) + relname | relkind | relpersistence +-----------------------+---------+---------------- + unlogged1 | r | u + unlogged1 toast index | i | u + unlogged1 toast table | t | u + unlogged1_pkey | i | u + unlogged2 | r | u + unlogged2_pkey | i | u + unlogged3 | r | u + unlogged3_pkey | i | u + unlogged4 | r | u + unlogged4_f1_seq | S | u + unlogged4_pkey | i | u +(11 rows) -CREATE UNLOGGED TABLE unlogged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged1); -- foreign key -CREATE UNLOGGED TABLE unlogged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged3); -- self-referencing foreign key ALTER TABLE unlogged3 SET LOGGED; -- skip self-referencing foreign key ALTER TABLE unlogged2 SET LOGGED; -- fails because a foreign key to an unlogged table exists ERROR: could not change table "unlogged2" to logged because it references unlogged table "unlogged1" ALTER TABLE unlogged1 SET LOGGED; +ALTER TABLE unlogged4 SET LOGGED; -- check relpersistence of an unlogged table after changing to permanent -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' +SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged[1-4]' UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' +SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged[1-4]' UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' +SELECT r.relname || ' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged[1-4]' ORDER BY relname; - relname | relkind | relpersistence -------------------+---------+---------------- - toast index | i | p - toast table | t | p - unlogged1 | r | p - unlogged1_f1_seq | S | p - unlogged1_pkey | i | p -(5 rows) + relname | relkind | relpersistence +-----------------------+---------+---------------- + unlogged1 | r | p + unlogged1 toast index | i | p + unlogged1 toast table | t | p + unlogged1_pkey | i | p + unlogged2 | r | u + unlogged2_pkey | i | u + unlogged3 | r | p + unlogged3_pkey | i | p + unlogged4 | r | p + unlogged4_f1_seq | S | p + unlogged4_pkey | i | p +(11 rows) ALTER TABLE unlogged1 SET LOGGED; -- silently do nothing DROP TABLE unlogged3; DROP TABLE unlogged2; DROP TABLE unlogged1; +DROP TABLE unlogged4; -- set unlogged CREATE TABLE logged1(f1 SERIAL PRIMARY KEY, f2 TEXT); +CREATE TABLE logged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged1); -- foreign key +CREATE TABLE logged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged3); -- self-referencing foreign key -- check relpersistence of a permanent table -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' +SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged[1-3]' UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' +SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged[1-3]' UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' +SELECT r.relname || 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged[1-3]' ORDER BY relname; - relname | relkind | relpersistence -----------------+---------+---------------- - logged1 | r | p - logged1_f1_seq | S | p - logged1_pkey | i | p - toast index | i | p - toast table | t | p -(5 rows) + relname | relkind | relpersistence +---------------------+---------+---------------- + logged1 | r | p + logged1 toast table | t | p + logged1_f1_seq | S | p + logged1_pkey | i | p + logged1toast index | i | p + logged2 | r | p + logged2_f1_seq | S | p + logged2_pkey | i | p + logged3 | r | p + logged3_f1_seq | S | p + logged3_pkey | i | p +(11 rows) -CREATE TABLE logged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged1); -- foreign key -CREATE TABLE logged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged3); -- self-referencing foreign key ALTER TABLE logged1 SET UNLOGGED; -- fails because a foreign key from a permanent table exists ERROR: could not change table "logged1" to unlogged because it references logged table "logged2" ALTER TABLE logged3 SET UNLOGGED; -- skip self-referencing foreign key ALTER TABLE logged2 SET UNLOGGED; ALTER TABLE logged1 SET UNLOGGED; -- check relpersistence of a permanent table after changing to unlogged -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' +SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged[1-3]' UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' +SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged[1-3]' UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' +SELECT r.relname || ' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged[1-3]' ORDER BY relname; - relname | relkind | relpersistence -----------------+---------+---------------- - logged1 | r | u - logged1_f1_seq | S | p - logged1_pkey | i | u - toast index | i | u - toast table | t | u -(5 rows) + relname | relkind | relpersistence +---------------------+---------+---------------- + logged1 | r | u + logged1 toast index | i | u + logged1 toast table | t | u + logged1_f1_seq | S | u + logged1_pkey | i | u + logged2 | r | u + logged2_f1_seq | S | u + logged2_pkey | i | u + logged3 | r | u + logged3_f1_seq | S | u + logged3_pkey | i | u +(11 rows) ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing DROP TABLE logged3; diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 71c2b0f1df..7cb2f7cc02 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; @@ -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; diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index cce1cb1dd3..a9d4dcb734 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -2180,51 +2180,54 @@ CREATE INDEX old_system_table__othercol ON old_system_table (othercol); DROP TABLE old_system_table; -- set logged -CREATE UNLOGGED TABLE unlogged1(f1 SERIAL PRIMARY KEY, f2 TEXT); +CREATE UNLOGGED TABLE unlogged1(f1 INTEGER PRIMARY KEY, f2 TEXT); +CREATE UNLOGGED TABLE unlogged2(f1 INTEGER PRIMARY KEY, f2 INTEGER REFERENCES unlogged1); -- foreign key +CREATE UNLOGGED TABLE unlogged3(f1 INTEGER PRIMARY KEY, f2 INTEGER REFERENCES unlogged3); -- self-referencing foreign key +CREATE UNLOGGED TABLE unlogged4(f1 SERIAL PRIMARY KEY); -- sequence -- check relpersistence of an unlogged table -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' +SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged[1-4]' UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' +SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged[1-4]' UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' +SELECT r.relname || ' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged[1-4]' ORDER BY relname; -CREATE UNLOGGED TABLE unlogged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged1); -- foreign key -CREATE UNLOGGED TABLE unlogged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged3); -- self-referencing foreign key ALTER TABLE unlogged3 SET LOGGED; -- skip self-referencing foreign key ALTER TABLE unlogged2 SET LOGGED; -- fails because a foreign key to an unlogged table exists ALTER TABLE unlogged1 SET LOGGED; +ALTER TABLE unlogged4 SET LOGGED; -- check relpersistence of an unlogged table after changing to permanent -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1' +SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged[1-4]' UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1' +SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged[1-4]' UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1' +SELECT r.relname || ' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged[1-4]' ORDER BY relname; ALTER TABLE unlogged1 SET LOGGED; -- silently do nothing DROP TABLE unlogged3; DROP TABLE unlogged2; DROP TABLE unlogged1; +DROP TABLE unlogged4; -- set unlogged CREATE TABLE logged1(f1 SERIAL PRIMARY KEY, f2 TEXT); +CREATE TABLE logged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged1); -- foreign key +CREATE TABLE logged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged3); -- self-referencing foreign key -- check relpersistence of a permanent table -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' +SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged[1-3]' UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' +SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged[1-3]' UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' +SELECT r.relname || 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged[1-3]' ORDER BY relname; -CREATE TABLE logged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged1); -- foreign key -CREATE TABLE logged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged3); -- self-referencing foreign key ALTER TABLE logged1 SET UNLOGGED; -- fails because a foreign key from a permanent table exists ALTER TABLE logged3 SET UNLOGGED; -- skip self-referencing foreign key ALTER TABLE logged2 SET UNLOGGED; ALTER TABLE logged1 SET UNLOGGED; -- check relpersistence of a permanent table after changing to unlogged -SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1' +SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged[1-3]' UNION ALL -SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1' +SELECT r.relname || ' toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged[1-3]' UNION ALL -SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1' +SELECT r.relname || ' toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged[1-3]' ORDER BY relname; ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing DROP TABLE logged3; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 7928ee23ee..674f5f1f66 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,15 @@ CREATE 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; base-commit: ac9c5dc5d4cc15e396b59ca66df25969e51a2a00 -- 2.35.1