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