From 76ff4ab188f830f75a018988dfb39e16853a2262 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Tue, 13 Sep 2022 10:53:39 +0300 Subject: [PATCH] Adds support for unlogged distributed sequences (#6292) We can now do the following: - Distribute sequence with logged/unlogged option - ALTER TABLE my_sequence SET LOGGED/UNLOGGED - ALTER SEQUENCE my_sequence SET LOGGED/UNLOGGED Relevant PG commit https://github.com/postgres/postgres/commit/344d62fb9a978a72cf8347f0369b9ee643fd0b31 --- .../commands/distribute_object_ops.c | 46 +++++ src/backend/distributed/commands/sequence.c | 115 ++++++++++++ src/backend/distributed/commands/table.c | 26 ++- .../distributed/deparser/citus_ruleutils.c | 7 +- .../deparser/deparse_sequence_stmts.c | 93 ++++++++++ .../deparser/qualify_sequence_stmt.c | 31 ++++ src/include/distributed/citus_ruleutils.h | 6 - src/include/distributed/commands.h | 11 ++ src/include/distributed/deparser.h | 6 + src/include/pg_version_compat.h | 9 + src/test/regress/expected/pg15.out | 170 ++++++++++++++++++ src/test/regress/sql/pg15.sql | 131 ++++++++++++++ 12 files changed, 641 insertions(+), 10 deletions(-) diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index 74a7ce69b..056ba20e2 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -734,6 +734,17 @@ static DistributeObjectOps Sequence_AlterOwner = { .address = AlterSequenceOwnerStmtObjectAddress, .markDistributed = false, }; +#if (PG_VERSION_NUM >= PG_VERSION_15) +static DistributeObjectOps Sequence_AlterPersistence = { + .deparse = DeparseAlterSequencePersistenceStmt, + .qualify = QualifyAlterSequencePersistenceStmt, + .preprocess = PreprocessAlterSequencePersistenceStmt, + .postprocess = NULL, + .operationType = DIST_OPS_ALTER, + .address = AlterSequencePersistenceStmtObjectAddress, + .markDistributed = false, +}; +#endif static DistributeObjectOps Sequence_Drop = { .deparse = DeparseDropSequenceStmt, .qualify = QualifyDropSequenceStmt, @@ -1463,6 +1474,41 @@ GetDistributeObjectOps(Node *node) case OBJECT_SEQUENCE: { +#if (PG_VERSION_NUM >= PG_VERSION_15) + ListCell *cmdCell = NULL; + foreach(cmdCell, stmt->cmds) + { + AlterTableCmd *cmd = castNode(AlterTableCmd, lfirst(cmdCell)); + switch (cmd->subtype) + { + case AT_ChangeOwner: + { + return &Sequence_AlterOwner; + } + + case AT_SetLogged: + { + return &Sequence_AlterPersistence; + } + + case AT_SetUnLogged: + { + return &Sequence_AlterPersistence; + } + + default: + { + return &NoDistributeOps; + } + } + } +#endif + + /* + * Prior to PG15, the only Alter Table statement + * with Sequence as its object was an + * Alter Owner statement + */ return &Sequence_AlterOwner; } diff --git a/src/backend/distributed/commands/sequence.c b/src/backend/distributed/commands/sequence.c index 9352ae297..20b7666ad 100644 --- a/src/backend/distributed/commands/sequence.c +++ b/src/backend/distributed/commands/sequence.c @@ -712,6 +712,121 @@ PostprocessAlterSequenceOwnerStmt(Node *node, const char *queryString) } +#if (PG_VERSION_NUM >= PG_VERSION_15) + +/* + * PreprocessAlterSequencePersistenceStmt is called for change of persistence + * of sequences before the persistence is changed on the local instance. + * + * If the sequence for which the persistence is changed is distributed, we execute + * the change on all the workers to keep the type in sync across the cluster. + */ +List * +PreprocessAlterSequencePersistenceStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext) +{ + AlterTableStmt *stmt = castNode(AlterTableStmt, node); + Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + + List *sequenceAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false, + false); + + /* the code-path only supports a single object */ + Assert(list_length(sequenceAddresses) == 1); + + if (!ShouldPropagateAnyObject(sequenceAddresses)) + { + return NIL; + } + + EnsureCoordinator(); + QualifyTreeNode((Node *) stmt); + + const char *sql = DeparseTreeNode((Node *) stmt); + + List *commands = list_make3(DISABLE_DDL_PROPAGATION, (void *) sql, + ENABLE_DDL_PROPAGATION); + + return NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands); +} + + +/* + * AlterSequencePersistenceStmtObjectAddress returns the ObjectAddress of the + * sequence that is the subject of the AlterPersistenceStmt. + */ +List * +AlterSequencePersistenceStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) +{ + AlterTableStmt *stmt = castNode(AlterTableStmt, node); + Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + + RangeVar *sequence = stmt->relation; + Oid seqOid = RangeVarGetRelid(sequence, NoLock, missing_ok); + ObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress)); + ObjectAddressSet(*sequenceAddress, RelationRelationId, seqOid); + + return list_make1(sequenceAddress); +} + + +/* + * PreprocessSequenceAlterTableStmt is called for change of persistence or owner + * of sequences before the persistence/owner is changed on the local instance. + * + * Altering persistence or owner are the only ALTER commands of a sequence + * that may pass through an AlterTableStmt as well + */ +List * +PreprocessSequenceAlterTableStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext) +{ + AlterTableStmt *stmt = castNode(AlterTableStmt, node); + Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + + ListCell *cmdCell = NULL; + foreach(cmdCell, stmt->cmds) + { + AlterTableCmd *cmd = castNode(AlterTableCmd, lfirst(cmdCell)); + switch (cmd->subtype) + { + case AT_ChangeOwner: + { + return PreprocessAlterSequenceOwnerStmt(node, + queryString, + processUtilityContext); + } + + case AT_SetLogged: + { + return PreprocessAlterSequencePersistenceStmt(node, + queryString, + processUtilityContext); + } + + case AT_SetUnLogged: + { + return PreprocessAlterSequencePersistenceStmt(node, + queryString, + processUtilityContext); + } + + default: + { + /* normally we shouldn't ever reach this */ + ereport(ERROR, (errmsg("unsupported subtype for alter sequence command"), + errdetail("sub command type: %d", + cmd->subtype))); + } + } + } + return NIL; +} + + +#endif + + /* * PreprocessGrantOnSequenceStmt is executed before the statement is applied to the local * postgres instance. diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 5b712dae2..e2a28fab6 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -733,20 +733,40 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand, /* * check whether we are dealing with a sequence or view here - * if yes, it must be ALTER TABLE .. OWNER TO .. command - * since this is the only ALTER command of a sequence or view that - * passes through an AlterTableStmt */ char relKind = get_rel_relkind(leftRelationId); if (relKind == RELKIND_SEQUENCE) { AlterTableStmt *stmtCopy = copyObject(alterTableStatement); AlterTableStmtObjType_compat(stmtCopy) = OBJECT_SEQUENCE; +#if (PG_VERSION_NUM >= PG_VERSION_15) + + /* + * it must be ALTER TABLE .. OWNER TO .. + * or ALTER TABLE .. SET LOGGED/UNLOGGED command + * since these are the only ALTER commands of a sequence that + * pass through an AlterTableStmt + */ + return PreprocessSequenceAlterTableStmt((Node *) stmtCopy, alterTableCommand, + processUtilityContext); +#else + + /* + * it must be ALTER TABLE .. OWNER TO .. command + * since this is the only ALTER command of a sequence that + * passes through an AlterTableStmt + */ return PreprocessAlterSequenceOwnerStmt((Node *) stmtCopy, alterTableCommand, processUtilityContext); +#endif } else if (relKind == RELKIND_VIEW) { + /* + * it must be ALTER TABLE .. OWNER TO .. command + * since this is the only ALTER command of a view that + * passes through an AlterTableStmt + */ AlterTableStmt *stmtCopy = copyObject(alterTableStatement); AlterTableStmtObjType_compat(stmtCopy) = OBJECT_VIEW; return PreprocessAlterViewStmt((Node *) stmtCopy, alterTableCommand, diff --git a/src/backend/distributed/deparser/citus_ruleutils.c b/src/backend/distributed/deparser/citus_ruleutils.c index dbb8edbe2..9534d7ac6 100644 --- a/src/backend/distributed/deparser/citus_ruleutils.c +++ b/src/backend/distributed/deparser/citus_ruleutils.c @@ -256,7 +256,12 @@ pg_get_sequencedef_string(Oid sequenceRelationId) char *qualifiedSequenceName = generate_qualified_relation_name(sequenceRelationId); char *typeName = format_type_be(pgSequenceForm->seqtypid); - char *sequenceDef = psprintf(CREATE_SEQUENCE_COMMAND, qualifiedSequenceName, + char *sequenceDef = psprintf(CREATE_SEQUENCE_COMMAND, +#if (PG_VERSION_NUM >= PG_VERSION_15) + get_rel_persistence(sequenceRelationId) == + RELPERSISTENCE_UNLOGGED ? "UNLOGGED " : "", +#endif + qualifiedSequenceName, typeName, pgSequenceForm->seqincrement, pgSequenceForm->seqmin, pgSequenceForm->seqmax, pgSequenceForm->seqstart, diff --git a/src/backend/distributed/deparser/deparse_sequence_stmts.c b/src/backend/distributed/deparser/deparse_sequence_stmts.c index 0a615d741..80c4e2dd4 100644 --- a/src/backend/distributed/deparser/deparse_sequence_stmts.c +++ b/src/backend/distributed/deparser/deparse_sequence_stmts.c @@ -27,6 +27,9 @@ static void AppendSequenceNameList(StringInfo buf, List *objects, ObjectType obj static void AppendRenameSequenceStmt(StringInfo buf, RenameStmt *stmt); static void AppendAlterSequenceSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt); static void AppendAlterSequenceOwnerStmt(StringInfo buf, AlterTableStmt *stmt); +#if (PG_VERSION_NUM >= PG_VERSION_15) +static void AppendAlterSequencePersistenceStmt(StringInfo buf, AlterTableStmt *stmt); +#endif static void AppendGrantOnSequenceStmt(StringInfo buf, GrantStmt *stmt); static void AppendGrantOnSequenceSequences(StringInfo buf, GrantStmt *stmt); @@ -258,6 +261,96 @@ AppendAlterSequenceOwnerStmt(StringInfo buf, AlterTableStmt *stmt) } +#if (PG_VERSION_NUM >= PG_VERSION_15) + +/* + * DeparseAlterSequencePersistenceStmt builds and returns a string representing + * the AlterTableStmt consisting of changing the persistence of a sequence + */ +char * +DeparseAlterSequencePersistenceStmt(Node *node) +{ + AlterTableStmt *stmt = castNode(AlterTableStmt, node); + StringInfoData str = { 0 }; + initStringInfo(&str); + + Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + + AppendAlterSequencePersistenceStmt(&str, stmt); + + return str.data; +} + + +/* + * AppendAlterSequencePersistenceStmt appends a string representing the + * AlterTableStmt to a buffer consisting of changing the persistence of a sequence + */ +static void +AppendAlterSequencePersistenceStmt(StringInfo buf, AlterTableStmt *stmt) +{ + Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + + RangeVar *seq = stmt->relation; + char *qualifiedSequenceName = quote_qualified_identifier(seq->schemaname, + seq->relname); + appendStringInfoString(buf, "ALTER SEQUENCE "); + + if (stmt->missing_ok) + { + appendStringInfoString(buf, "IF EXISTS "); + } + + appendStringInfoString(buf, qualifiedSequenceName); + + ListCell *cmdCell = NULL; + foreach(cmdCell, stmt->cmds) + { + if (cmdCell != list_head(stmt->cmds)) + { + /* + * As of PG15, we cannot reach this code because ALTER SEQUENCE + * is only supported for a single sequence. Still, let's be + * defensive for future PG changes + */ + ereport(ERROR, (errmsg("More than one subcommand is not supported " + "for ALTER SEQUENCE"))); + } + + AlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(cmdCell)); + switch (alterTableCmd->subtype) + { + case AT_SetLogged: + { + appendStringInfoString(buf, " SET LOGGED;"); + break; + } + + case AT_SetUnLogged: + { + appendStringInfoString(buf, " SET UNLOGGED;"); + break; + } + + default: + { + /* + * normally we shouldn't ever reach this + * because we enter this function after making sure this stmt is of the form + * ALTER SEQUENCE .. SET LOGGED/UNLOGGED + */ + ereport(ERROR, (errmsg("unsupported subtype for alter sequence command"), + errdetail("sub command type: %d", + alterTableCmd->subtype))); + } + } + } +} + + +#endif + + /* * DeparseGrantOnSequenceStmt builds and returns a string representing the GrantOnSequenceStmt */ diff --git a/src/backend/distributed/deparser/qualify_sequence_stmt.c b/src/backend/distributed/deparser/qualify_sequence_stmt.c index 9f4ef6fe8..cece902a6 100644 --- a/src/backend/distributed/deparser/qualify_sequence_stmt.c +++ b/src/backend/distributed/deparser/qualify_sequence_stmt.c @@ -51,6 +51,37 @@ QualifyAlterSequenceOwnerStmt(Node *node) } +#if (PG_VERSION_NUM >= PG_VERSION_15) + +/* + * QualifyAlterSequencePersistenceStmt transforms a + * ALTER SEQUENCE .. SET LOGGED/UNLOGGED + * statement in place and makes the sequence name fully qualified. + */ +void +QualifyAlterSequencePersistenceStmt(Node *node) +{ + AlterTableStmt *stmt = castNode(AlterTableStmt, node); + Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + + RangeVar *seq = stmt->relation; + + if (seq->schemaname == NULL) + { + Oid seqOid = RangeVarGetRelid(seq, NoLock, stmt->missing_ok); + + if (OidIsValid(seqOid)) + { + Oid schemaOid = get_rel_namespace(seqOid); + seq->schemaname = get_namespace_name(schemaOid); + } + } +} + + +#endif + + /* * QualifyAlterSequenceSchemaStmt transforms a * ALTER SEQUENCE .. SET SCHEMA .. diff --git a/src/include/distributed/citus_ruleutils.h b/src/include/distributed/citus_ruleutils.h index 938a70578..ca06e6d5a 100644 --- a/src/include/distributed/citus_ruleutils.h +++ b/src/include/distributed/citus_ruleutils.h @@ -20,12 +20,6 @@ #include "nodes/parsenodes.h" #include "nodes/pg_list.h" - -#define CREATE_SEQUENCE_COMMAND \ - "CREATE SEQUENCE IF NOT EXISTS %s AS %s INCREMENT BY " INT64_FORMAT \ - " MINVALUE " INT64_FORMAT " MAXVALUE " INT64_FORMAT \ - " START WITH " INT64_FORMAT " CACHE " INT64_FORMAT " %sCYCLE" - /* Function declarations for version independent Citus ruleutils wrapper functions */ extern char * pg_get_extensiondef_string(Oid tableRelationId); extern Oid get_extension_schema(Oid ext_oid); diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index e67669ab2..656feec67 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -459,6 +459,13 @@ extern List * PostprocessAlterSequenceSchemaStmt(Node *node, const char *querySt extern List * PreprocessAlterSequenceOwnerStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext); extern List * PostprocessAlterSequenceOwnerStmt(Node *node, const char *queryString); +#if (PG_VERSION_NUM >= PG_VERSION_15) +extern List * PreprocessAlterSequencePersistenceStmt(Node *node, const char *queryString, + ProcessUtilityContext + processUtilityContext); +extern List * PreprocessSequenceAlterTableStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext); +#endif extern List * PreprocessDropSequenceStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext); extern List * SequenceDropStmtObjectAddress(Node *stmt, bool missing_ok, bool @@ -474,6 +481,10 @@ extern List * AlterSequenceSchemaStmtObjectAddress(Node *node, bool missing_ok, isPostprocess); extern List * AlterSequenceOwnerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess); +#if (PG_VERSION_NUM >= PG_VERSION_15) +extern List * AlterSequencePersistenceStmtObjectAddress(Node *node, bool missing_ok, bool + isPostprocess); +#endif extern List * RenameSequenceStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess); extern void ErrorIfUnsupportedSeqStmt(CreateSeqStmt *createSeqStmt); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index 9ac15b6ac..0d0a99e22 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -226,6 +226,9 @@ extern char * DeparseDropSequenceStmt(Node *node); extern char * DeparseRenameSequenceStmt(Node *node); extern char * DeparseAlterSequenceSchemaStmt(Node *node); extern char * DeparseAlterSequenceOwnerStmt(Node *node); +#if (PG_VERSION_NUM >= PG_VERSION_15) +extern char * DeparseAlterSequencePersistenceStmt(Node *node); +#endif extern char * DeparseGrantOnSequenceStmt(Node *node); /* forward declarations for qualify_sequence_stmt.c */ @@ -233,6 +236,9 @@ extern void QualifyRenameSequenceStmt(Node *node); extern void QualifyDropSequenceStmt(Node *node); extern void QualifyAlterSequenceSchemaStmt(Node *node); extern void QualifyAlterSequenceOwnerStmt(Node *node); +#if (PG_VERSION_NUM >= PG_VERSION_15) +extern void QualifyAlterSequencePersistenceStmt(Node *node); +#endif extern void QualifyGrantOnSequenceStmt(Node *node); #endif /* CITUS_DEPARSER_H */ diff --git a/src/include/pg_version_compat.h b/src/include/pg_version_compat.h index f551085a7..fcb857c41 100644 --- a/src/include/pg_version_compat.h +++ b/src/include/pg_version_compat.h @@ -18,6 +18,10 @@ #define RelationCreateStorage_compat(a, b, c) RelationCreateStorage(a, b, c) #define parse_analyze_varparams_compat(a, b, c, d, e) parse_analyze_varparams(a, b, c, d, \ e) +#define CREATE_SEQUENCE_COMMAND \ + "CREATE %sSEQUENCE IF NOT EXISTS %s AS %s INCREMENT BY " INT64_FORMAT \ + " MINVALUE " INT64_FORMAT " MAXVALUE " INT64_FORMAT \ + " START WITH " INT64_FORMAT " CACHE " INT64_FORMAT " %sCYCLE" #else #include "nodes/value.h" @@ -62,6 +66,11 @@ RelationGetSmgr(Relation rel) } +#define CREATE_SEQUENCE_COMMAND \ + "CREATE SEQUENCE IF NOT EXISTS %s AS %s INCREMENT BY " INT64_FORMAT \ + " MINVALUE " INT64_FORMAT " MAXVALUE " INT64_FORMAT \ + " START WITH " INT64_FORMAT " CACHE " INT64_FORMAT " %sCYCLE" + #endif #if PG_VERSION_NUM >= PG_VERSION_14 diff --git a/src/test/regress/expected/pg15.out b/src/test/regress/expected/pg15.out index 3c596bbb1..e535d198d 100644 --- a/src/test/regress/expected/pg15.out +++ b/src/test/regress/expected/pg15.out @@ -552,6 +552,176 @@ SELECT count(*)=100 FROM copy_test2; t (1 row) +-- +-- In PG15, unlogged sequences are supported +-- we support this for distributed sequences as well +-- +CREATE SEQUENCE seq1; +CREATE UNLOGGED SEQUENCE "pg15"."seq 2"; +-- first, test that sequence persistence is distributed correctly +-- when the sequence is distributed +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + relname | logged_info +--------------------------------------------------------------------- + seq 2 | unlogged + seq1 | logged +(2 rows) + +CREATE TABLE "seq test"(a int, b int default nextval ('seq1'), c int default nextval ('"pg15"."seq 2"')); +SELECT create_distributed_table('"pg15"."seq test"','a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +\c - - - :worker_1_port +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + relname | logged_info +--------------------------------------------------------------------- + seq 2 | unlogged + seq1 | logged +(2 rows) + +\c - - - :master_port +SET search_path TO pg15; +-- now, check that we can change sequence persistence using ALTER SEQUENCE +ALTER SEQUENCE seq1 SET UNLOGGED; +-- use IF EXISTS +ALTER SEQUENCE IF EXISTS "seq 2" SET LOGGED; +-- check non-existent sequence as well +ALTER SEQUENCE seq_non_exists SET LOGGED; +ERROR: relation "seq_non_exists" does not exist +ALTER SEQUENCE IF EXISTS seq_non_exists SET LOGGED; +NOTICE: relation "seq_non_exists" does not exist, skipping +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + relname | logged_info +--------------------------------------------------------------------- + seq 2 | logged + seq1 | unlogged +(2 rows) + +\c - - - :worker_1_port +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + relname | logged_info +--------------------------------------------------------------------- + seq 2 | logged + seq1 | unlogged +(2 rows) + +\c - - - :master_port +SET search_path TO pg15; +-- now, check that we can change sequence persistence using ALTER TABLE +ALTER TABLE seq1 SET LOGGED; +ALTER TABLE "seq 2" SET UNLOGGED; +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + relname | logged_info +--------------------------------------------------------------------- + seq 2 | unlogged + seq1 | logged +(2 rows) + +\c - - - :worker_1_port +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + relname | logged_info +--------------------------------------------------------------------- + seq 2 | unlogged + seq1 | logged +(2 rows) + +\c - - - :master_port +SET search_path TO pg15; +-- An identity/serial sequence now automatically gets and follows the +-- persistence level (logged/unlogged) of its owning table. +-- Test this behavior as well +CREATE UNLOGGED TABLE test(a bigserial, b bigserial); +SELECT create_distributed_table('test', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- show that associated sequence is unlooged +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('test_a_seq', 'test_b_seq') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + relname | logged_info +--------------------------------------------------------------------- + test_a_seq | unlogged + test_b_seq | unlogged +(2 rows) + +\c - - - :worker_1_port +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('test_a_seq', 'test_b_seq') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + relname | logged_info +--------------------------------------------------------------------- + test_a_seq | unlogged + test_b_seq | unlogged +(2 rows) + +\c - - - :master_port +SET search_path TO pg15; -- allow foreign key columns to have SET NULL/DEFAULT on column basis -- currently only reference tables can support that CREATE TABLE PKTABLE (tid int, id int, PRIMARY KEY (tid, id)); diff --git a/src/test/regress/sql/pg15.sql b/src/test/regress/sql/pg15.sql index 9cfa5960d..80b23986d 100644 --- a/src/test/regress/sql/pg15.sql +++ b/src/test/regress/sql/pg15.sql @@ -298,6 +298,137 @@ ALTER TABLE copy_test2 RENAME COLUMN data_ TO data; COPY copy_test2 FROM :'temp_dir''copy_test.txt' WITH ( HEADER match, FORMAT text); SELECT count(*)=100 FROM copy_test2; +-- +-- In PG15, unlogged sequences are supported +-- we support this for distributed sequences as well +-- + +CREATE SEQUENCE seq1; +CREATE UNLOGGED SEQUENCE "pg15"."seq 2"; + +-- first, test that sequence persistence is distributed correctly +-- when the sequence is distributed + +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + +CREATE TABLE "seq test"(a int, b int default nextval ('seq1'), c int default nextval ('"pg15"."seq 2"')); + +SELECT create_distributed_table('"pg15"."seq test"','a'); + +\c - - - :worker_1_port +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + +\c - - - :master_port +SET search_path TO pg15; + +-- now, check that we can change sequence persistence using ALTER SEQUENCE + +ALTER SEQUENCE seq1 SET UNLOGGED; +-- use IF EXISTS +ALTER SEQUENCE IF EXISTS "seq 2" SET LOGGED; +-- check non-existent sequence as well +ALTER SEQUENCE seq_non_exists SET LOGGED; +ALTER SEQUENCE IF EXISTS seq_non_exists SET LOGGED; + +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + +\c - - - :worker_1_port +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + +\c - - - :master_port +SET search_path TO pg15; + +-- now, check that we can change sequence persistence using ALTER TABLE +ALTER TABLE seq1 SET LOGGED; +ALTER TABLE "seq 2" SET UNLOGGED; + +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + +\c - - - :worker_1_port +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('seq1', 'seq 2') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + +\c - - - :master_port +SET search_path TO pg15; + +-- An identity/serial sequence now automatically gets and follows the +-- persistence level (logged/unlogged) of its owning table. +-- Test this behavior as well + +CREATE UNLOGGED TABLE test(a bigserial, b bigserial); +SELECT create_distributed_table('test', 'a'); + +-- show that associated sequence is unlooged +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('test_a_seq', 'test_b_seq') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + +\c - - - :worker_1_port +SELECT relname, + CASE relpersistence + WHEN 'u' THEN 'unlogged' + WHEN 'p' then 'logged' + ELSE 'unknown' + END AS logged_info +FROM pg_class +WHERE relname IN ('test_a_seq', 'test_b_seq') AND relnamespace='pg15'::regnamespace +ORDER BY relname; + +\c - - - :master_port +SET search_path TO pg15; -- allow foreign key columns to have SET NULL/DEFAULT on column basis -- currently only reference tables can support that