From 9ddfe034c7d7ee9c4a7dcae1ba37e5e4e5ac2488 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Thu, 25 Oct 2007 18:54:03 +0000 Subject: [PATCH] Fix ALTER SEQUENCE so that it does not affect the value of currval() for the sequence. Also, make setval() with is_called = false not affect the currval state, either. Per report from Kris Jurka that an implicit ALTER SEQUENCE OWNED BY unexpectedly caused currval() to become valid. Since this isn't 100% backwards compatible, it will go into HEAD only; I'll put a more limited patch into 8.2. --- doc/src/sgml/func.sgml | 23 ++++++++++------- doc/src/sgml/ref/alter_sequence.sgml | 13 ++++++++-- src/backend/commands/sequence.c | 37 ++++++++++++++++++---------- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index c9d665376e4..5feb572de2d 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.404 2007/10/23 20:46:11 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.405 2007/10/25 18:54:03 tgl Exp $ --> <chapter id="functions"> <title>Functions and Operators</title> @@ -8733,15 +8733,20 @@ nextval('foo'::text) <lineannotation><literal>foo</literal> is looked up at <listitem> <para> Reset the sequence object's counter value. The two-parameter - form sets the sequence's <literal>last_value</literal> field to the specified - value and sets its <literal>is_called</literal> field to <literal>true</literal>, - meaning that the next <function>nextval</function> will advance the sequence - before returning a value. In the three-parameter form, - <literal>is_called</literal> can be set either <literal>true</literal> or - <literal>false</literal>. If it's set to <literal>false</literal>, - the next <function>nextval</function> will return exactly the specified + form sets the sequence's <literal>last_value</literal> field to the + specified value and sets its <literal>is_called</literal> field to + <literal>true</literal>, meaning that the next + <function>nextval</function> will advance the sequence before + returning a value. The value reported by <function>currval</> is + also set to the specified value. In the three-parameter form, + <literal>is_called</literal> can be set either <literal>true</literal> + or <literal>false</literal>. <literal>true</> has the same effect as + the two-parameter form. If it's set to <literal>false</literal>, the + next <function>nextval</function> will return exactly the specified value, and sequence advancement commences with the following - <function>nextval</function>. For example, + <function>nextval</function>. Furthermore, the value reported by + <function>currval</> is not changed in this case (this is a change + from pre-8.3 behavior). For example, <screen> SELECT setval('foo', 42); <lineannotation>Next <function>nextval</> will return 43</lineannotation> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index b515ae46016..3c982eee6f1 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/alter_sequence.sgml,v 1.18 2007/10/03 16:48:43 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/alter_sequence.sgml,v 1.19 2007/10/25 18:54:03 tgl Exp $ PostgreSQL documentation --> @@ -114,7 +114,10 @@ ALTER SEQUENCE <replaceable class="parameter">name</replaceable> SET SCHEMA <rep <para> The optional clause <literal>RESTART WITH <replaceable class="parameter">start</replaceable></literal> changes the - current value of the sequence. + current value of the sequence. This is equivalent to calling the + <function>setval</> function with <literal>is_called</literal> = + <literal>false</>: the specified value will be returned by the + <emphasis>next</> call of <function>nextval</>. </para> </listitem> </varlistentry> @@ -226,6 +229,12 @@ ALTER SEQUENCE <replaceable class="parameter">name</replaceable> SET SCHEMA <rep immediately. </para> + <para> + <command>ALTER SEQUENCE</command> does not affect the <function>currval</> + status for the sequence. (Before <productname>PostgreSQL</productname> + 8.3, it sometimes did.) + </para> + <para> Some variants of <command>ALTER TABLE</command> can be used with sequences as well; for example, to rename a sequence it is also diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 25d1e2311b6..619e289206b 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.146 2007/09/20 17:56:31 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.147 2007/10/25 18:54:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -65,10 +65,12 @@ typedef struct SeqTableData struct SeqTableData *next; /* link to next SeqTable object */ Oid relid; /* pg_class OID of this sequence */ LocalTransactionId lxid; /* xact in which we last did a seq op */ + bool last_valid; /* do we have a valid "last" value? */ int64 last; /* value last returned by nextval */ int64 cached; /* last value already cached for nextval */ /* if last != cached, we have not used up all the cached values */ int64 increment; /* copy of sequence's increment field */ + /* note that increment is zero until we first do read_info() */ } SeqTableData; typedef SeqTableData *SeqTable; @@ -336,14 +338,13 @@ AlterSequence(AlterSeqStmt *stmt) /* Check and set new values */ init_params(stmt->options, false, &new, &owned_by); + /* Clear local cache so that we don't think we have cached numbers */ + /* Note that we do not change the currval() state */ + elm->cached = elm->last; + /* Now okay to update the on-disk tuple */ memcpy(seq, &new, sizeof(FormData_pg_sequence)); - /* Clear local cache so that we don't think we have cached numbers */ - elm->last = new.last_value; /* last returned number */ - elm->cached = new.last_value; /* last cached number (forget cached - * values) */ - START_CRIT_SECTION(); MarkBufferDirty(buf); @@ -443,9 +444,11 @@ nextval_internal(Oid relid) if (elm->last != elm->cached) /* some numbers were cached */ { - last_used_seq = elm; + Assert(elm->last_valid); + Assert(elm->increment != 0); elm->last += elm->increment; relation_close(seqrel, NoLock); + last_used_seq = elm; return elm->last; } @@ -564,6 +567,7 @@ nextval_internal(Oid relid) /* save info in local cache */ elm->last = result; /* last returned number */ elm->cached = last; /* last fetched number */ + elm->last_valid = true; last_used_seq = elm; @@ -633,7 +637,7 @@ currval_oid(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - if (elm->increment == 0) /* nextval/read_info were not called */ + if (!elm->last_valid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("currval of sequence \"%s\" is not yet defined in this session", @@ -668,7 +672,7 @@ lastval(PG_FUNCTION_ARGS) seqrel = open_share_lock(last_used_seq); /* nextval() must have already been called for this sequence */ - Assert(last_used_seq->increment != 0); + Assert(last_used_seq->last_valid); if (pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK && pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_USAGE) != ACLCHECK_OK) @@ -732,9 +736,15 @@ do_setval(Oid relid, int64 next, bool iscalled) bufm, bufx))); } - /* save info in local cache */ - elm->last = next; /* last returned number */ - elm->cached = next; /* last cached number (forget cached values) */ + /* Set the currval() state only if iscalled = true */ + if (iscalled) + { + elm->last = next; /* last returned number */ + elm->last_valid = true; + } + + /* In any case, forget any future cached numbers */ + elm->cached = elm->last; START_CRIT_SECTION(); @@ -893,7 +903,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel) errmsg("out of memory"))); elm->relid = relid; elm->lxid = InvalidLocalTransactionId; - /* increment is set to 0 until we do read_info (see currval) */ + elm->last_valid = false; elm->last = elm->cached = elm->increment = 0; elm->next = seqtab; seqtab = elm; @@ -941,6 +951,7 @@ read_info(SeqTable elm, Relation rel, Buffer *buf) seq = (Form_pg_sequence) GETSTRUCT(&tuple); + /* this is a handy place to update our copy of the increment */ elm->increment = seq->increment_by; return seq; -- GitLab