diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/sequence.c | 5 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 9 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 80 | ||||
-rw-r--r-- | src/bin/pg_dump/pg_dump.c | 17 | ||||
-rw-r--r-- | src/test/regress/expected/identity.out | 16 | ||||
-rw-r--r-- | src/test/regress/sql/identity.sql | 9 |
6 files changed, 100 insertions, 36 deletions
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index b37fd688d34..0188e8bbd5b 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1347,7 +1347,10 @@ init_params(ParseState *pstate, List *options, bool for_identity, /* * The parser allows this, but it is only for identity columns, in * which case it is filtered out in parse_utilcmd.c. We only get - * here if someone puts it into a CREATE SEQUENCE. + * here if someone puts it into a CREATE SEQUENCE, where it'd be + * redundant. (The same is true for the equally-nonstandard + * LOGGED and UNLOGGED options, but for those, the default error + * below seems sufficient.) */ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index d587f6dcd98..ab304ca989d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -4936,6 +4936,10 @@ SeqOptElem: AS SimpleTypename { $$ = makeDefElem("increment", (Node *) $3, @1); } + | LOGGED + { + $$ = makeDefElem("logged", NULL, @1); + } | MAXVALUE NumericOnly { $$ = makeDefElem("maxvalue", (Node *) $2, @1); @@ -4958,7 +4962,6 @@ SeqOptElem: AS SimpleTypename } | SEQUENCE NAME_P any_name { - /* not documented, only used by pg_dump */ $$ = makeDefElem("sequence_name", (Node *) $3, @1); } | START opt_with NumericOnly @@ -4973,6 +4976,10 @@ SeqOptElem: AS SimpleTypename { $$ = makeDefElem("restart", (Node *) $3, @1); } + | UNLOGGED + { + $$ = makeDefElem("unlogged", NULL, @1); + } ; opt_by: BY diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 15274607542..1e15ce10b48 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -365,30 +365,22 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, { ListCell *option; DefElem *nameEl = NULL; + DefElem *loggedEl = NULL; Oid snamespaceid; char *snamespace; char *sname; + char seqpersistence; CreateSeqStmt *seqstmt; AlterSeqStmt *altseqstmt; List *attnamelist; - int nameEl_idx = -1; /* Make a copy of this as we may end up modifying it in the code below */ seqoptions = list_copy(seqoptions); /* - * Determine namespace and name to use for the sequence. - * - * First, check if a sequence name was passed in as an option. This is - * used by pg_dump. Else, generate a name. - * - * Although we use ChooseRelationName, it's not guaranteed that the - * selected sequence name won't conflict; given sufficiently long field - * names, two different serial columns in the same table could be assigned - * the same sequence name, and we'd not notice since we aren't creating - * the sequence quite yet. In practice this seems quite unlikely to be a - * problem, especially since few people would need two serial columns in - * one table. + * Check for non-SQL-standard options (not supported within CREATE + * SEQUENCE, because they'd be redundant), and remove them from the + * seqoptions list if found. */ foreach(option, seqoptions) { @@ -399,12 +391,24 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, if (nameEl) errorConflictingDefElem(defel, cxt->pstate); nameEl = defel; - nameEl_idx = foreach_current_index(option); + seqoptions = foreach_delete_current(seqoptions, option); + } + else if (strcmp(defel->defname, "logged") == 0 || + strcmp(defel->defname, "unlogged") == 0) + { + if (loggedEl) + errorConflictingDefElem(defel, cxt->pstate); + loggedEl = defel; + seqoptions = foreach_delete_current(seqoptions, option); } } + /* + * Determine namespace and name to use for the sequence. + */ if (nameEl) { + /* Use specified name */ RangeVar *rv = makeRangeVarFromNameList(castNode(List, nameEl->arg)); snamespace = rv->schemaname; @@ -418,11 +422,20 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, snamespace = get_namespace_name(snamespaceid); } sname = rv->relname; - /* Remove the SEQUENCE NAME item from seqoptions */ - seqoptions = list_delete_nth_cell(seqoptions, nameEl_idx); } else { + /* + * Generate a name. + * + * Although we use ChooseRelationName, it's not guaranteed that the + * selected sequence name won't conflict; given sufficiently long + * field names, two different serial columns in the same table could + * be assigned the same sequence name, and we'd not notice since we + * aren't creating the sequence quite yet. In practice this seems + * quite unlikely to be a problem, especially since few people would + * need two serial columns in one table. + */ if (cxt->rel) snamespaceid = RelationGetNamespace(cxt->rel); else @@ -444,22 +457,37 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, cxt->relation->relname, column->colname))); /* + * Determine the persistence of the sequence. By default we copy the + * persistence of the table, but if LOGGED or UNLOGGED was specified, use + * that (as long as the table isn't TEMP). + * + * For CREATE TABLE, we get the persistence from cxt->relation, which + * comes from the CreateStmt in progress. For ALTER TABLE, the parser + * won't set cxt->relation->relpersistence, but we have cxt->rel as the + * existing table, so we copy the persistence from there. + */ + seqpersistence = cxt->rel ? cxt->rel->rd_rel->relpersistence : cxt->relation->relpersistence; + if (loggedEl) + { + if (seqpersistence == RELPERSISTENCE_TEMP) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot set logged status of a temporary sequence"), + parser_errposition(cxt->pstate, loggedEl->location))); + else if (strcmp(loggedEl->defname, "logged") == 0) + seqpersistence = RELPERSISTENCE_PERMANENT; + else + seqpersistence = RELPERSISTENCE_UNLOGGED; + } + + /* * Build a CREATE SEQUENCE command to create the sequence object, and add * it to the list of things to be done before this CREATE/ALTER TABLE. */ seqstmt = makeNode(CreateSeqStmt); seqstmt->for_identity = for_identity; seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - - /* - * Copy the persistence of the table. For CREATE TABLE, we get the - * persistence from cxt->relation, which comes from the CreateStmt in - * progress. For ALTER TABLE, the parser won't set - * cxt->relation->relpersistence, but we have cxt->rel as the existing - * table, so we copy the persistence from there. - */ - seqstmt->sequence->relpersistence = cxt->rel ? cxt->rel->rd_rel->relpersistence : cxt->relation->relpersistence; - + seqstmt->sequence->relpersistence = seqpersistence; seqstmt->options = seqoptions; /* diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 6e07984e8d5..130b80775db 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -17568,6 +17568,15 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo) appendPQExpBufferStr(query, "BY DEFAULT"); appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n", fmtQualifiedDumpable(tbinfo)); + + /* + * Emit persistence option only if it's different from the owning + * table's. This avoids using this new syntax unnecessarily. + */ + if (tbinfo->relpersistence != owning_tab->relpersistence) + appendPQExpBuffer(query, " %s\n", + tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ? + "UNLOGGED" : "LOGGED"); } else { @@ -17600,15 +17609,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo) seq->cache, (seq->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"); diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out index 3d554fe3276..f14bfccfb1d 100644 --- a/src/test/regress/expected/identity.out +++ b/src/test/regress/expected/identity.out @@ -905,3 +905,19 @@ SELECT * FROM itest16; DROP TABLE itest15; DROP TABLE itest16; +-- For testing of pg_dump and pg_upgrade, leave behind some identity +-- sequences whose logged-ness doesn't match their owning table's. +CREATE TABLE identity_dump_logged (a INT GENERATED ALWAYS AS IDENTITY); +ALTER SEQUENCE identity_dump_logged_a_seq SET UNLOGGED; +CREATE UNLOGGED TABLE identity_dump_unlogged (a INT GENERATED ALWAYS AS IDENTITY); +ALTER SEQUENCE identity_dump_unlogged_a_seq SET LOGGED; +SELECT relname, relpersistence FROM pg_class + WHERE relname ~ '^identity_dump_' ORDER BY 1; + relname | relpersistence +------------------------------+---------------- + identity_dump_logged | p + identity_dump_logged_a_seq | u + identity_dump_unlogged | u + identity_dump_unlogged_a_seq | p +(4 rows) + diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql index 84c43a19a31..cb0e05a2f11 100644 --- a/src/test/regress/sql/identity.sql +++ b/src/test/regress/sql/identity.sql @@ -528,3 +528,12 @@ SELECT * FROM itest15; SELECT * FROM itest16; DROP TABLE itest15; DROP TABLE itest16; + +-- For testing of pg_dump and pg_upgrade, leave behind some identity +-- sequences whose logged-ness doesn't match their owning table's. +CREATE TABLE identity_dump_logged (a INT GENERATED ALWAYS AS IDENTITY); +ALTER SEQUENCE identity_dump_logged_a_seq SET UNLOGGED; +CREATE UNLOGGED TABLE identity_dump_unlogged (a INT GENERATED ALWAYS AS IDENTITY); +ALTER SEQUENCE identity_dump_unlogged_a_seq SET LOGGED; +SELECT relname, relpersistence FROM pg_class + WHERE relname ~ '^identity_dump_' ORDER BY 1; |