From 10a3471bed7b57fb986a5be8afdee5f0dda419de Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 16 May 2008 23:36:05 +0000
Subject: [PATCH] Add a RESTART (without parameter) option to ALTER SEQUENCE,
 allowing a sequence to be reset to its original starting value.  This
 requires adding the original start value to the set of parameters (columns)
 of a sequence object, which is a user-visible change with potential
 compatibility implications; it also forces initdb.

Also add hopefully-SQL-compatible RESTART/CONTINUE IDENTITY options to
TRUNCATE TABLE.  RESTART IDENTITY executes ALTER SEQUENCE RESTART for all
sequences "owned by" any of the truncated relations.  CONTINUE IDENTITY is
a no-op option.

Zoltan Boszormenyi
---
 doc/src/sgml/ref/alter_sequence.sgml   |  11 ++-
 doc/src/sgml/ref/truncate.sgml         |  60 ++++++++++--
 src/backend/catalog/pg_depend.c        |  54 ++++++++++-
 src/backend/commands/sequence.c        | 129 +++++++++++++++++++------
 src/backend/commands/tablecmds.c       |  57 ++++++++++-
 src/backend/nodes/copyfuncs.c          |   3 +-
 src/backend/nodes/equalfuncs.c         |   3 +-
 src/backend/parser/gram.y              |  31 ++++--
 src/backend/parser/keywords.c          |   4 +-
 src/bin/pg_dump/pg_dump.c              |  83 +++++++++++-----
 src/include/catalog/catversion.h       |   4 +-
 src/include/catalog/dependency.h       |   4 +-
 src/include/commands/sequence.h        |  31 +++---
 src/include/nodes/parsenodes.h         |   3 +-
 src/test/regress/expected/sequence.out |  63 ++++++++++--
 src/test/regress/expected/truncate.out |  38 ++++++++
 src/test/regress/sql/sequence.sql      |  15 ++-
 src/test/regress/sql/truncate.sql      |  26 +++++
 18 files changed, 513 insertions(+), 106 deletions(-)

diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml
index 3c982eee6f1..31e64dac35d 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.19 2007/10/25 18:54:03 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_sequence.sgml,v 1.20 2008/05/16 23:36:04 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -26,7 +26,7 @@ PostgreSQL documentation
   <synopsis>
 ALTER SEQUENCE <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ]
     [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
-    [ RESTART [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
+    [ RESTART [ [ WITH ] <replaceable class="parameter">start</replaceable> ] ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
     [ OWNED BY { <replaceable class="parameter">table</replaceable>.<replaceable class="parameter">column</replaceable> | NONE } ]
 ALTER SEQUENCE <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
 ALTER SEQUENCE <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
@@ -112,12 +112,15 @@ ALTER SEQUENCE <replaceable class="parameter">name</replaceable> SET SCHEMA <rep
       <term><replaceable class="parameter">start</replaceable></term>
       <listitem>
        <para>
-        The optional clause <literal>RESTART WITH <replaceable
-        class="parameter">start</replaceable></literal> changes the
+        The optional clause <literal>RESTART [ WITH <replaceable
+        class="parameter">start</replaceable> ]</literal> changes the
         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</>.
+        Writing <literal>RESTART</> with no <replaceable
+        class="parameter">start</replaceable> value is equivalent to supplying
+        the start value used when the sequence was created.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/ref/truncate.sgml b/doc/src/sgml/ref/truncate.sgml
index 486a2d3e992..effe903b099 100644
--- a/doc/src/sgml/ref/truncate.sgml
+++ b/doc/src/sgml/ref/truncate.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.25 2008/03/28 00:21:55 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.26 2008/05/16 23:36:04 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -20,7 +20,8 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ... ]
+    [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -50,6 +51,25 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>RESTART IDENTITY</literal></term>
+    <listitem>
+     <para>
+      Automatically restart sequences owned by columns of
+      the truncated table(s).
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CONTINUE IDENTITY</literal></term>
+    <listitem>
+     <para>
+      Do not change the values of sequences.  This is the default.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>CASCADE</literal></term>
     <listitem>
@@ -66,7 +86,7 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C
     <listitem>
      <para>
       Refuse to truncate if any of the tables have foreign-key references
-      from tables that are not to be truncated.  This is the default.
+      from tables that are not listed in the command.  This is the default.
      </para>
     </listitem>
    </varlistentry>
@@ -119,11 +139,23 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C
      cause visible inconsistency between the contents of the truncated
      table and other tables in the database.
    </para>
+  </warning>
+
+  <para>
+   <command>TRUNCATE</> is transaction-safe with respect to the data
+   in the tables: the truncation will be safely rolled back if the surrounding
+   transaction does not commit.
+  </para>
 
+  <warning>
    <para>
-    <command>TRUNCATE</> is transaction-safe, however: the truncation
-    will be safely rolled back if the surrounding transaction does not
-    commit.
+    Any <command>ALTER SEQUENCE RESTART</> operations performed as a
+    consequence of using the <literal>RESTART IDENTITY</> option are
+    nontransactional and will not be rolled back.  To minimize risk,
+    these operations are performed only after all the rest of
+    <command>TRUNCATE</>'s work is done.  In practice this will only
+    be an issue if <command>TRUNCATE</> is performed inside a
+    transaction block that is aborted afterwards.
    </para>
   </warning>
  </refsect1>
@@ -132,13 +164,22 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ C
   <title>Examples</title>
 
   <para>
-   Truncate the tables <literal>bigtable</literal> and <literal>fattable</literal>:
+   Truncate the tables <literal>bigtable</literal> and
+   <literal>fattable</literal>:
 
 <programlisting>
 TRUNCATE bigtable, fattable;
 </programlisting>
   </para>
 
+  <para>
+   The same, and also reset any associated sequence generators:
+
+<programlisting>
+TRUNCATE bigtable, fattable RESTART IDENTITY;
+</programlisting>
+  </para>
+
   <para>
    Truncate the table <literal>othertable</literal>, and cascade to any tables
    that reference <literal>othertable</literal> via foreign-key
@@ -154,7 +195,10 @@ TRUNCATE othertable CASCADE;
   <title>Compatibility</title>
 
   <para>
-   There is no <command>TRUNCATE</command> command in the SQL standard.
+   The draft SQL:2008 standard includes a <command>TRUNCATE</command> command,
+   but at this writing it is uncertain whether that will reach standardization
+   or be fully compatible with <productname>PostgreSQL</productname>'s
+   implementation.
   </para>
  </refsect1>
 </refentry>
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 32d8d07044b..d1fd8f5200f 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.27 2008/03/26 21:10:37 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.28 2008/05/16 23:36:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -418,6 +418,58 @@ markSequenceUnowned(Oid seqId)
 	heap_close(depRel, RowExclusiveLock);
 }
 
+/*
+ * Collect a list of OIDs of all sequences owned by the specified relation.
+ */
+List *
+getOwnedSequences(Oid relid)
+{
+	List	   *result = NIL;
+	Relation	depRel;
+	ScanKeyData	key[2];
+	SysScanDesc	scan;
+	HeapTuple	tup;
+
+	depRel = heap_open(DependRelationId, AccessShareLock);
+
+	ScanKeyInit(&key[0],
+				Anum_pg_depend_refclassid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationRelationId));
+	ScanKeyInit(&key[1],
+				Anum_pg_depend_refobjid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
+
+	scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+							  SnapshotNow, 2, key);
+
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+
+		/*
+		 * We assume any auto dependency of a sequence on a column must be
+		 * what we are looking for.  (We need the relkind test because indexes
+		 * can also have auto dependencies on columns.)
+		 */
+		if (deprec->classid == RelationRelationId &&
+			deprec->objsubid == 0 &&
+			deprec->refobjsubid != 0 &&
+			deprec->deptype == DEPENDENCY_AUTO &&
+			get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
+		{
+			result = lappend_oid(result, deprec->objid);
+		}
+	}
+
+	systable_endscan(scan);
+
+	heap_close(depRel, AccessShareLock);
+
+	return result;
+}
+
 
 /*
  * get_constraint_index
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 670fcbcfbdb..748413ebed3 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.150 2008/05/12 00:00:47 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.151 2008/05/16 23:36:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -91,7 +91,7 @@ static Relation open_share_lock(SeqTable seq);
 static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
 static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
 static void init_params(List *options, bool isInit,
-			Form_pg_sequence new, List **owned_by);
+			Form_pg_sequence new, Form_pg_sequence old, List **owned_by);
 static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by);
 
@@ -119,10 +119,10 @@ DefineSequence(CreateSeqStmt *seq)
 	NameData	name;
 
 	/* Check and set all option values */
-	init_params(seq->options, true, &new, &owned_by);
+	init_params(seq->options, true, &new, NULL, &owned_by);
 
 	/*
-	 * Create relation (and fill *null & *value)
+	 * Create relation (and fill value[] and null[] for the tuple)
 	 */
 	stmt->tableElts = NIL;
 	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
@@ -151,6 +151,11 @@ DefineSequence(CreateSeqStmt *seq)
 				coldef->colname = "last_value";
 				value[i - 1] = Int64GetDatumFast(new.last_value);
 				break;
+			case SEQ_COL_STARTVAL:
+				coldef->typename = makeTypeNameFromOid(INT8OID, -1);
+				coldef->colname = "start_value";
+				value[i - 1] = Int64GetDatumFast(new.start_value);
+				break;
 			case SEQ_COL_INCBY:
 				coldef->typename = makeTypeNameFromOid(INT8OID, -1);
 				coldef->colname = "increment_by";
@@ -314,6 +319,29 @@ void
 AlterSequence(AlterSeqStmt *stmt)
 {
 	Oid			relid;
+
+	/* find sequence */
+	relid = RangeVarGetRelid(stmt->sequence, false);
+
+	/* allow ALTER to sequence owner only */
+	/* if you change this, see also callers of AlterSequenceInternal! */
+	if (!pg_class_ownercheck(relid, GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+					   stmt->sequence->relname);
+
+	/* do the work */
+	AlterSequenceInternal(relid, stmt->options);
+}
+
+/*
+ * AlterSequenceInternal
+ *
+ * Same as AlterSequence except that the sequence is specified by OID
+ * and we assume the caller already checked permissions.
+ */
+void
+AlterSequenceInternal(Oid relid, List *options)
+{
 	SeqTable	elm;
 	Relation	seqrel;
 	Buffer		buf;
@@ -323,23 +351,14 @@ AlterSequence(AlterSeqStmt *stmt)
 	List	   *owned_by;
 
 	/* open and AccessShareLock sequence */
-	relid = RangeVarGetRelid(stmt->sequence, false);
 	init_sequence(relid, &elm, &seqrel);
 
-	/* allow ALTER to sequence owner only */
-	if (!pg_class_ownercheck(elm->relid, GetUserId()))
-		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
-					   stmt->sequence->relname);
-
 	/* lock page' buffer and read tuple into new sequence structure */
 	seq = read_info(elm, seqrel, &buf);
 	page = BufferGetPage(buf);
 
-	/* Copy old values of options into workspace */
-	memcpy(&new, seq, sizeof(FormData_pg_sequence));
-
-	/* Check and set new values */
-	init_params(stmt->options, false, &new, &owned_by);
+	/* Fill workspace with appropriate new info */
+	init_params(options, false, &new, seq, &owned_by);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
@@ -970,7 +989,7 @@ read_info(SeqTable elm, Relation rel, Buffer *buf)
  */
 static void
 init_params(List *options, bool isInit,
-			Form_pg_sequence new, List **owned_by)
+			Form_pg_sequence new, Form_pg_sequence old, List **owned_by)
 {
 	DefElem    *last_value = NULL;
 	DefElem    *increment_by = NULL;
@@ -982,6 +1001,12 @@ init_params(List *options, bool isInit,
 
 	*owned_by = NIL;
 
+	/* Copy old values of options into workspace */
+	if (old != NULL)
+		memcpy(new, old, sizeof(FormData_pg_sequence));
+	else
+		memset(new, 0, sizeof(FormData_pg_sequence));
+
 	foreach(option, options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(option);
@@ -994,13 +1019,24 @@ init_params(List *options, bool isInit,
 						 errmsg("conflicting or redundant options")));
 			increment_by = defel;
 		}
-
-		/*
-		 * start is for a new sequence restart is for alter
-		 */
-		else if (strcmp(defel->defname, "start") == 0 ||
-				 strcmp(defel->defname, "restart") == 0)
+		else if (strcmp(defel->defname, "start") == 0)
+		{
+			if (!isInit)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("use RESTART not START in ALTER SEQUENCE")));
+			if (last_value)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			last_value = defel;
+		}
+		else if (strcmp(defel->defname, "restart") == 0)
 		{
+			if (isInit)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("use START not RESTART in CREATE SEQUENCE")));
 			if (last_value)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1109,24 +1145,59 @@ init_params(List *options, bool isInit,
 						bufm, bufx)));
 	}
 
-	/* START WITH */
+	/* START/RESTART [WITH] */
 	if (last_value != NULL)
 	{
-		new->last_value = defGetInt64(last_value);
+		if (last_value->arg != NULL)
+			new->last_value = defGetInt64(last_value);
+		else
+		{
+			Assert(old != NULL);
+			new->last_value = old->start_value;
+		}
+		if (isInit)
+			new->start_value = new->last_value;
 		new->is_called = false;
 		new->log_cnt = 1;
 	}
 	else if (isInit)
 	{
 		if (new->increment_by > 0)
-			new->last_value = new->min_value;	/* ascending seq */
+			new->start_value = new->min_value;	/* ascending seq */
 		else
-			new->last_value = new->max_value;	/* descending seq */
+			new->start_value = new->max_value;	/* descending seq */
+		new->last_value = new->start_value;
 		new->is_called = false;
 		new->log_cnt = 1;
 	}
 
-	/* crosscheck */
+	/* crosscheck START */
+	if (new->start_value < new->min_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("START value (%s) cannot be less than MINVALUE (%s)",
+						bufs, bufm)));
+	}
+	if (new->start_value > new->max_value)
+	{
+		char		bufs[100],
+					bufm[100];
+
+		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
+		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			  errmsg("START value (%s) cannot be greater than MAXVALUE (%s)",
+					 bufs, bufm)));
+	}
+
+	/* must crosscheck RESTART separately */
 	if (new->last_value < new->min_value)
 	{
 		char		bufs[100],
@@ -1136,7 +1207,7 @@ init_params(List *options, bool isInit,
 		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("START value (%s) cannot be less than MINVALUE (%s)",
+				 errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
 						bufs, bufm)));
 	}
 	if (new->last_value > new->max_value)
@@ -1148,7 +1219,7 @@ init_params(List *options, bool isInit,
 		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-			  errmsg("START value (%s) cannot be greater than MAXVALUE (%s)",
+			  errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
 					 bufs, bufm)));
 	}
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 48e229e0fb5..0bef48e07bf 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.253 2008/05/12 00:00:48 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.254 2008/05/16 23:36:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,7 @@
 #include "catalog/toasting.h"
 #include "commands/cluster.h"
 #include "commands/defrem.h"
+#include "commands/sequence.h"
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
@@ -531,6 +532,7 @@ ExecuteTruncate(TruncateStmt *stmt)
 {
 	List	   *rels = NIL;
 	List	   *relids = NIL;
+	List	   *seq_relids = NIL;
 	EState	   *estate;
 	ResultRelInfo *resultRelInfos;
 	ResultRelInfo *resultRelInfo;
@@ -596,6 +598,40 @@ ExecuteTruncate(TruncateStmt *stmt)
 		heap_truncate_check_FKs(rels, false);
 #endif
 
+	/*
+	 * If we are asked to restart sequences, find all the sequences,
+	 * lock them (we only need AccessShareLock because that's all that
+	 * ALTER SEQUENCE takes), and check permissions.  We want to do this
+	 * early since it's pointless to do all the truncation work only to fail
+	 * on sequence permissions.
+	 */
+	if (stmt->restart_seqs)
+	{
+		foreach(cell, rels)
+		{
+			Relation	rel = (Relation) lfirst(cell);
+			List	   *seqlist = getOwnedSequences(RelationGetRelid(rel));
+			ListCell   *seqcell;
+
+			foreach(seqcell, seqlist)
+			{
+				Oid		seq_relid = lfirst_oid(seqcell);
+				Relation seq_rel;
+
+				seq_rel = relation_open(seq_relid, AccessShareLock);
+
+				/* This check must match AlterSequence! */
+				if (!pg_class_ownercheck(seq_relid, GetUserId()))
+					aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+								   RelationGetRelationName(seq_rel));
+
+				seq_relids = lappend_oid(seq_relids, seq_relid);
+
+				relation_close(seq_rel, NoLock);
+			}
+		}
+	}
+
 	/* Prepare to catch AFTER triggers. */
 	AfterTriggerBeginQuery();
 
@@ -694,6 +730,25 @@ ExecuteTruncate(TruncateStmt *stmt)
 
 		heap_close(rel, NoLock);
 	}
+
+	/*
+	 * Lastly, restart any owned sequences if we were asked to.  This is done
+	 * last because it's nontransactional: restarts will not roll back if
+	 * we abort later.  Hence it's important to postpone them as long as
+	 * possible.  (This is also a big reason why we locked and
+	 * permission-checked the sequences beforehand.)
+	 */
+	if (stmt->restart_seqs)
+	{
+		List   *options = list_make1(makeDefElem("restart", NULL));
+
+		foreach(cell, seq_relids)
+		{
+			Oid		seq_relid = lfirst_oid(cell);
+
+			AlterSequenceInternal(seq_relid, options);
+		}
+	}
 }
 
 /*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 67dcf9a2182..a42c40327f2 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.393 2008/04/29 14:59:16 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.394 2008/05/16 23:36:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2156,6 +2156,7 @@ _copyTruncateStmt(TruncateStmt *from)
 	TruncateStmt *newnode = makeNode(TruncateStmt);
 
 	COPY_NODE_FIELD(relations);
+	COPY_SCALAR_FIELD(restart_seqs);
 	COPY_SCALAR_FIELD(behavior);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 0c444901322..435ee6a6afb 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.322 2008/04/29 14:59:16 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.323 2008/05/16 23:36:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1010,6 +1010,7 @@ static bool
 _equalTruncateStmt(TruncateStmt *a, TruncateStmt *b)
 {
 	COMPARE_NODE_FIELD(relations);
+	COMPARE_SCALAR_FIELD(restart_seqs);
 	COMPARE_SCALAR_FIELD(behavior);
 
 	return true;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 56a56627e7f..591920d6f68 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.614 2008/04/29 20:44:49 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.615 2008/05/16 23:36:05 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -206,7 +206,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 %type <str>		OptSchemaName
 %type <list>	OptSchemaEltList
 
-%type <boolean> TriggerActionTime TriggerForSpec opt_trusted
+%type <boolean> TriggerActionTime TriggerForSpec opt_trusted opt_restart_seqs
 %type <str>		opt_lancompiler
 
 %type <str>		TriggerEvents
@@ -381,7 +381,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
 	CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
 	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
-	CONTENT_P CONVERSION_P COPY COST CREATE CREATEDB
+	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
 	CREATEROLE CREATEUSER CROSS CSV CURRENT_P CURRENT_DATE CURRENT_ROLE
 	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
 
@@ -399,10 +399,10 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 
 	HANDLER HAVING HEADER_P HOLD HOUR_P
 
-	IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
-	INDEX INDEXES INHERIT INHERITS INITIALLY INNER_P INOUT INPUT_P
-	INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT
-	INTERVAL INTO INVOKER IS ISNULL ISOLATION
+	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P
+	INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY
+	INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
+	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
 
 	JOIN
 
@@ -2489,6 +2489,10 @@ OptSeqElem: CACHE NumericOnly
 				{
 					$$ = makeDefElem("start", (Node *)$3);
 				}
+			| RESTART
+				{
+					$$ = makeDefElem("restart", NULL);
+				}
 			| RESTART opt_with NumericOnly
 				{
 					$$ = makeDefElem("restart", (Node *)$3);
@@ -3364,15 +3368,22 @@ attrs:		'.' attr_name
  *****************************************************************************/
 
 TruncateStmt:
-			TRUNCATE opt_table qualified_name_list opt_drop_behavior
+			TRUNCATE opt_table qualified_name_list opt_restart_seqs opt_drop_behavior
 				{
 					TruncateStmt *n = makeNode(TruncateStmt);
 					n->relations = $3;
-					n->behavior = $4;
+					n->restart_seqs = $4;
+					n->behavior = $5;
 					$$ = (Node *)n;
 				}
 		;
 
+opt_restart_seqs:
+			CONTINUE_P IDENTITY_P		{ $$ = false; }
+			| RESTART IDENTITY_P		{ $$ = true; }
+			| /* EMPTY */				{ $$ = false; }
+		;
+
 /*****************************************************************************
  *
  *	The COMMENT ON statement can take different forms based upon the type of
@@ -8964,6 +8975,7 @@ unreserved_keyword:
 			| CONNECTION
 			| CONSTRAINTS
 			| CONTENT_P
+			| CONTINUE_P
 			| CONVERSION_P
 			| COPY
 			| COST
@@ -9014,6 +9026,7 @@ unreserved_keyword:
 			| HEADER_P
 			| HOLD
 			| HOUR_P
+			| IDENTITY_P
 			| IF_P
 			| IMMEDIATE
 			| IMMUTABLE
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index b5312a487fd..46b306b98d1 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.195 2008/03/27 03:57:33 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.196 2008/05/16 23:36:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -101,6 +101,7 @@ static const ScanKeyword ScanKeywords[] = {
 	{"constraint", CONSTRAINT, RESERVED_KEYWORD},
 	{"constraints", CONSTRAINTS, UNRESERVED_KEYWORD},
 	{"content", CONTENT_P, UNRESERVED_KEYWORD},
+	{"continue", CONTINUE_P, UNRESERVED_KEYWORD},
 	{"conversion", CONVERSION_P, UNRESERVED_KEYWORD},
 	{"copy", COPY, UNRESERVED_KEYWORD},
 	{"cost", COST, UNRESERVED_KEYWORD},
@@ -181,6 +182,7 @@ static const ScanKeyword ScanKeywords[] = {
 	{"header", HEADER_P, UNRESERVED_KEYWORD},
 	{"hold", HOLD, UNRESERVED_KEYWORD},
 	{"hour", HOUR_P, UNRESERVED_KEYWORD},
+	{"identity", IDENTITY_P, UNRESERVED_KEYWORD},
 	{"if", IF_P, UNRESERVED_KEYWORD},
 	{"ilike", ILIKE, TYPE_FUNC_NAME_KEYWORD},
 	{"immediate", IMMEDIATE, UNRESERVED_KEYWORD},
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4aefd058234..4122dad8455 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
  *	by PostgreSQL
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.491 2008/05/12 00:00:53 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.492 2008/05/16 23:36:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -9525,7 +9525,8 @@ static void
 dumpSequence(Archive *fout, TableInfo *tbinfo)
 {
 	PGresult   *res;
-	char	   *last,
+	char	   *startv,
+			   *last,
 			   *incby,
 			   *maxv = NULL,
 			   *minv = NULL,
@@ -9543,19 +9544,40 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
 	snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE);
 
-	appendPQExpBuffer(query,
-					  "SELECT sequence_name, last_value, increment_by, "
-				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
-				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
-					  "     ELSE max_value "
-					  "END AS max_value, "
-					"CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
-				   "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
-					  "     ELSE min_value "
-					  "END AS min_value, "
-					  "cache_value, is_cycled, is_called from %s",
-					  bufx, bufm,
-					  fmtId(tbinfo->dobj.name));
+	if (g_fout->remoteVersion >= 80400)
+	{
+		appendPQExpBuffer(query,
+				  "SELECT sequence_name, "
+				  "start_value, last_value, increment_by, "
+				  "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
+				  "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
+				  "     ELSE max_value "
+				  "END AS max_value, "
+				  "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
+				  "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
+				  "     ELSE min_value "
+				  "END AS min_value, "
+				  "cache_value, is_cycled, is_called from %s",
+				  bufx, bufm,
+				  fmtId(tbinfo->dobj.name));
+	}
+	else
+	{
+		appendPQExpBuffer(query,
+				  "SELECT sequence_name, "
+				  "0 as start_value, last_value, increment_by, "
+				  "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
+				  "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
+				  "     ELSE max_value "
+				  "END AS max_value, "
+				  "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
+				  "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
+				  "     ELSE min_value "
+				  "END AS min_value, "
+				  "cache_value, is_cycled, is_called from %s",
+				  bufx, bufm,
+				  fmtId(tbinfo->dobj.name));
+	}
 
 	res = PQexec(g_conn, query->data);
 	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
@@ -9577,15 +9599,16 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	}
 #endif
 
-	last = PQgetvalue(res, 0, 1);
-	incby = PQgetvalue(res, 0, 2);
-	if (!PQgetisnull(res, 0, 3))
-		maxv = PQgetvalue(res, 0, 3);
+	startv = PQgetvalue(res, 0, 1);
+	last = PQgetvalue(res, 0, 2);
+	incby = PQgetvalue(res, 0, 3);
 	if (!PQgetisnull(res, 0, 4))
-		minv = PQgetvalue(res, 0, 4);
-	cache = PQgetvalue(res, 0, 5);
-	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
-	called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
+		maxv = PQgetvalue(res, 0, 4);
+	if (!PQgetisnull(res, 0, 5))
+		minv = PQgetvalue(res, 0, 5);
+	cache = PQgetvalue(res, 0, 6);
+	cycled = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
+	called = (strcmp(PQgetvalue(res, 0, 8), "t") == 0);
 
 	/*
 	 * The logic we use for restoring sequences is as follows:
@@ -9615,8 +9638,18 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 						  "CREATE SEQUENCE %s\n",
 						  fmtId(tbinfo->dobj.name));
 
-		if (!called)
-			appendPQExpBuffer(query, "    START WITH %s\n", last);
+		if (g_fout->remoteVersion >= 80400)
+			appendPQExpBuffer(query, "    START WITH %s\n", startv);
+		else
+		{
+			/*
+			 * Versions before 8.4 did not remember the true start value.  If
+			 * is_called is false then the sequence has never been incremented
+			 * so we can use last_val.  Otherwise punt and let it default.
+			 */
+			if (!called)
+				appendPQExpBuffer(query, "    START WITH %s\n", last);
+		}
 
 		appendPQExpBuffer(query, "    INCREMENT BY %s\n", incby);
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 94d70c2c877..e5b87fa864d 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.460 2008/05/16 16:31:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.461 2008/05/16 23:36:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200805161
+#define CATALOG_VERSION_NO	200805162
 
 #endif
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 572dedbc4fe..e192c1ed288 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.34 2008/03/24 19:47:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.35 2008/05/16 23:36:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -207,6 +207,8 @@ extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId);
 
 extern void markSequenceUnowned(Oid seqId);
 
+extern List *getOwnedSequences(Oid relid);
+
 extern Oid	get_constraint_index(Oid constraintId);
 
 extern Oid	get_index_constraint(Oid indexId);
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index cf95b6394af..fddf9b9ace7 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/sequence.h,v 1.40 2008/03/27 03:57:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/sequence.h,v 1.41 2008/05/16 23:36:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@ typedef struct FormData_pg_sequence
 	NameData	sequence_name;
 #ifndef INT64_IS_BUSTED
 	int64		last_value;
+	int64		start_value;
 	int64		increment_by;
 	int64		max_value;
 	int64		min_value;
@@ -38,16 +39,18 @@ typedef struct FormData_pg_sequence
 #else
 	int32		last_value;
 	int32		pad1;
-	int32		increment_by;
+	int32		start_value;
 	int32		pad2;
-	int32		max_value;
+	int32		increment_by;
 	int32		pad3;
-	int32		min_value;
+	int32		max_value;
 	int32		pad4;
-	int32		cache_value;
+	int32		min_value;
 	int32		pad5;
-	int32		log_cnt;
+	int32		cache_value;
 	int32		pad6;
+	int32		log_cnt;
+	int32		pad7;
 #endif
 	bool		is_cycled;
 	bool		is_called;
@@ -61,13 +64,14 @@ typedef FormData_pg_sequence *Form_pg_sequence;
 
 #define SEQ_COL_NAME			1
 #define SEQ_COL_LASTVAL			2
-#define SEQ_COL_INCBY			3
-#define SEQ_COL_MAXVALUE		4
-#define SEQ_COL_MINVALUE		5
-#define SEQ_COL_CACHE			6
-#define SEQ_COL_LOG				7
-#define SEQ_COL_CYCLE			8
-#define SEQ_COL_CALLED			9
+#define SEQ_COL_STARTVAL		3
+#define SEQ_COL_INCBY			4
+#define SEQ_COL_MAXVALUE		5
+#define SEQ_COL_MINVALUE		6
+#define SEQ_COL_CACHE			7
+#define SEQ_COL_LOG				8
+#define SEQ_COL_CYCLE			9
+#define SEQ_COL_CALLED			10
 
 #define SEQ_COL_FIRSTCOL		SEQ_COL_NAME
 #define SEQ_COL_LASTCOL			SEQ_COL_CALLED
@@ -90,6 +94,7 @@ extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern void DefineSequence(CreateSeqStmt *stmt);
 extern void AlterSequence(AlterSeqStmt *stmt);
+extern void AlterSequenceInternal(Oid relid, List *options);
 
 extern void seq_redo(XLogRecPtr lsn, XLogRecord *rptr);
 extern void seq_desc(StringInfo buf, uint8 xl_info, char *rec);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0875b99a224..472c07b5081 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.365 2008/05/09 23:32:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.366 2008/05/16 23:36:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1452,6 +1452,7 @@ typedef struct TruncateStmt
 {
 	NodeTag		type;
 	List	   *relations;		/* relations (RangeVars) to be truncated */
+	bool		restart_seqs;	/* restart owned sequences? */
 	DropBehavior behavior;		/* RESTRICT or CASCADE behavior */
 } TruncateStmt;
 
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 0576b575ee6..84ece98357b 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -99,9 +99,27 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
- sequence_name | last_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
----------------+------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq       |          1 |            1 | 9223372036854775807 |         1 |           1 |       1 | f         | f
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
+ foo_seq       |          1 |           1 |            1 | 9223372036854775807 |         1 |           1 |       1 | f         | f
+(1 row)
+
+SELECT nextval('foo_seq_new');
+ nextval 
+---------
+       1
+(1 row)
+
+SELECT nextval('foo_seq_new');
+ nextval 
+---------
+       2
+(1 row)
+
+SELECT * FROM foo_seq_new;
+ sequence_name | last_value | start_value | increment_by |      max_value      | min_value | cache_value | log_cnt | is_cycled | is_called 
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
+ foo_seq       |          2 |           1 |            1 | 9223372036854775807 |         1 |           1 |      32 | f         | t
 (1 row)
 
 DROP SEQUENCE foo_seq_new;
@@ -155,18 +173,49 @@ SELECT nextval('sequence_test2');
       32
 (1 row)
 
-ALTER SEQUENCE sequence_test2 RESTART WITH 16
-	 INCREMENT BY 4 MAXVALUE 22 MINVALUE 5 CYCLE;
+ALTER SEQUENCE sequence_test2 RESTART WITH 24
+	 INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE;
+SELECT nextval('sequence_test2');
+ nextval 
+---------
+      24
+(1 row)
+
 SELECT nextval('sequence_test2');
  nextval 
 ---------
-      16
+      28
+(1 row)
+
+SELECT nextval('sequence_test2');
+ nextval 
+---------
+      32
+(1 row)
+
+SELECT nextval('sequence_test2');
+ nextval 
+---------
+      36
+(1 row)
+
+SELECT nextval('sequence_test2');
+ nextval 
+---------
+       5
+(1 row)
+
+ALTER SEQUENCE sequence_test2 RESTART;
+SELECT nextval('sequence_test2');
+ nextval 
+---------
+      32
 (1 row)
 
 SELECT nextval('sequence_test2');
  nextval 
 ---------
-      20
+      36
 (1 row)
 
 SELECT nextval('sequence_test2');
diff --git a/src/test/regress/expected/truncate.out b/src/test/regress/expected/truncate.out
index ed6182c69f9..929aba4da70 100644
--- a/src/test/regress/expected/truncate.out
+++ b/src/test/regress/expected/truncate.out
@@ -223,3 +223,41 @@ SELECT * FROM trunc_trigger_log;
 DROP TABLE trunc_trigger_test;
 DROP TABLE trunc_trigger_log;
 DROP FUNCTION trunctrigger();
+-- test TRUNCATE ... RESTART IDENTITY
+CREATE SEQUENCE truncate_a_id1 START WITH 33;
+CREATE TABLE truncate_a (id serial,
+                         id1 integer default nextval('truncate_a_id1'));
+NOTICE:  CREATE TABLE will create implicit sequence "truncate_a_id_seq" for serial column "truncate_a.id"
+ALTER SEQUENCE truncate_a_id1 OWNED BY truncate_a.id1;
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+ id | id1 
+----+-----
+  1 |  33
+  2 |  34
+(2 rows)
+
+TRUNCATE truncate_a;
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+ id | id1 
+----+-----
+  3 |  35
+  4 |  36
+(2 rows)
+
+TRUNCATE truncate_a RESTART IDENTITY;
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+ id | id1 
+----+-----
+  1 |  33
+  2 |  34
+(2 rows)
+
+DROP TABLE truncate_a;
+SELECT nextval('truncate_a_id1'); -- fail, seq should have been dropped
+ERROR:  relation "truncate_a_id1" does not exist
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 806a7181926..f3eb81ec134 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -33,6 +33,9 @@ DROP SEQUENCE sequence_test;
 CREATE SEQUENCE foo_seq;
 ALTER TABLE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
+SELECT nextval('foo_seq_new');
+SELECT nextval('foo_seq_new');
+SELECT * FROM foo_seq_new;
 DROP SEQUENCE foo_seq_new;
 
 -- renaming serial sequences
@@ -68,8 +71,16 @@ CREATE SEQUENCE sequence_test2 START WITH 32;
 
 SELECT nextval('sequence_test2');
 
-ALTER SEQUENCE sequence_test2 RESTART WITH 16
-	 INCREMENT BY 4 MAXVALUE 22 MINVALUE 5 CYCLE;
+ALTER SEQUENCE sequence_test2 RESTART WITH 24
+	 INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE;
+SELECT nextval('sequence_test2');
+SELECT nextval('sequence_test2');
+SELECT nextval('sequence_test2');
+SELECT nextval('sequence_test2');
+SELECT nextval('sequence_test2');
+
+ALTER SEQUENCE sequence_test2 RESTART;
+
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
 SELECT nextval('sequence_test2');
diff --git a/src/test/regress/sql/truncate.sql b/src/test/regress/sql/truncate.sql
index e60349e2073..3cce3ee314b 100644
--- a/src/test/regress/sql/truncate.sql
+++ b/src/test/regress/sql/truncate.sql
@@ -130,3 +130,29 @@ DROP TABLE trunc_trigger_test;
 DROP TABLE trunc_trigger_log;
 
 DROP FUNCTION trunctrigger();
+
+-- test TRUNCATE ... RESTART IDENTITY
+CREATE SEQUENCE truncate_a_id1 START WITH 33;
+CREATE TABLE truncate_a (id serial,
+                         id1 integer default nextval('truncate_a_id1'));
+ALTER SEQUENCE truncate_a_id1 OWNED BY truncate_a.id1;
+
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+
+TRUNCATE truncate_a;
+
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+
+TRUNCATE truncate_a RESTART IDENTITY;
+
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+
+DROP TABLE truncate_a;
+
+SELECT nextval('truncate_a_id1'); -- fail, seq should have been dropped
-- 
GitLab