aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/sequence.c5
-rw-r--r--src/backend/parser/gram.y9
-rw-r--r--src/backend/parser/parse_utilcmd.c80
-rw-r--r--src/bin/pg_dump/pg_dump.c17
-rw-r--r--src/test/regress/expected/identity.out16
-rw-r--r--src/test/regress/sql/identity.sql9
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;