From c012df68bdfc8711d142f7a91f8ea0ee4ecef813 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Thu, 11 Aug 2016 12:00:00 -0400
Subject: [PATCH 1/2] Add NEXT VALUE FOR expression

This is the SQL-standard-conforming version of the nextval function.  It
functions the same way, but has a more SQL-like syntax and separate
parse and executor nodes.
---
 doc/src/sgml/func.sgml                 | 29 ++++++++++++++++++++++++++---
 doc/src/sgml/ref/create_sequence.sgml  | 17 ++++++++---------
 src/backend/catalog/dependency.c       |  7 +++++++
 src/backend/commands/sequence.c        |  3 +--
 src/backend/executor/execQual.c        | 21 +++++++++++++++++++++
 src/backend/nodes/copyfuncs.c          | 34 ++++++++++++++++++++++++++++++++++
 src/backend/nodes/equalfuncs.c         | 24 ++++++++++++++++++++++++
 src/backend/nodes/nodeFuncs.c          | 18 ++++++++++++++++++
 src/backend/nodes/outfuncs.c           | 12 ++++++++++++
 src/backend/nodes/readfuncs.c          | 16 ++++++++++++++++
 src/backend/parser/gram.y              | 12 ++++++++++++
 src/backend/parser/parse_expr.c        | 23 +++++++++++++++++++++++
 src/backend/parser/parse_target.c      |  3 +++
 src/backend/utils/adt/ruleutils.c      |  9 +++++++++
 src/bin/psql/tab-complete.c            |  6 ++++++
 src/include/commands/sequence.h        |  2 ++
 src/include/nodes/nodes.h              |  2 ++
 src/include/nodes/parsenodes.h         | 10 ++++++++++
 src/include/nodes/primnodes.h          | 10 ++++++++++
 src/test/regress/expected/sequence.out |  8 ++++----
 src/test/regress/sql/sequence.sql      |  2 +-
 21 files changed, 249 insertions(+), 19 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 426e562..dc21e6b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -11476,6 +11476,9 @@ <title>Sequence Manipulation Functions</title>
    <primary>sequence</primary>
   </indexterm>
   <indexterm>
+   <primary>NEXT VALUE FOR</primary>
+  </indexterm>
+  <indexterm>
    <primary>nextval</primary>
   </indexterm>
   <indexterm>
@@ -11489,7 +11492,7 @@ <title>Sequence Manipulation Functions</title>
   </indexterm>
 
   <para>
-   This section describes functions for operating on <firstterm>sequence
+   This section describes functions and other expressions for operating on <firstterm>sequence
    objects</firstterm>, also called sequence generators or just sequences.
    Sequence objects are special single-row tables created with <xref
    linkend="sql-createsequence">.
@@ -11512,7 +11515,7 @@ <title>Sequence Functions</title>
         <entry><literal><function>currval(<type>regclass</type>)</function></literal></entry>
         <entry><type>bigint</type></entry>
         <entry>Return value most recently obtained with
-        <function>nextval</function> for specified sequence</entry>
+        <function>nextval</function>/<literal>NEXT VALUE FOR</literal> for specified sequence</entry>
       </row>
       <row>
         <entry><literal><function>lastval()</function></literal></entry>
@@ -11526,6 +11529,11 @@ <title>Sequence Functions</title>
         <entry>Advance sequence and return new value</entry>
       </row>
       <row>
+        <entry><literal>NEXT VALUE FOR <replaceable>sequence_name</replaceable></literal></entry>
+        <entry><type>bigint</type></entry>
+        <entry>Advance sequence and return new value</entry>
+      </row>
+      <row>
         <entry><literal><function>setval(<type>regclass</type>, <type>bigint</type>)</function></literal></entry>
         <entry><type>bigint</type></entry>
         <entry>Set sequence's current value</entry>
@@ -11540,7 +11548,8 @@ <title>Sequence Functions</title>
    </table>
 
   <para>
-   The sequence to be operated on by a sequence function is specified by
+   For the function syntax,
+   the sequence to be operated on by a sequence function is specified by
    a <type>regclass</> argument, which is simply the OID of the sequence in the
    <structname>pg_class</> system catalog.  You do not have to look up the
    OID by hand, however, since the <type>regclass</> data type's input
@@ -11601,11 +11610,21 @@ <title>Sequence Functions</title>
   </note>
 
   <para>
+   For <literal>NEXT VALUE FOR</literal>, the sequence argument is a regular
+   identifier, e.g.,
+<programlisting>
+NEXT VALUE FOR foo
+NEXT VALUE FOR "Foo"
+</programlisting>
+  </para>
+
+  <para>
    The available sequence functions are:
 
     <variablelist>
      <varlistentry>
       <term><function>nextval</function></term>
+      <term><literal>NEXT VALUE FOR</literal></term>
       <listitem>
        <para>
         Advance the sequence object to its next value and return that
@@ -11640,6 +11659,10 @@ <title>Sequence Functions</title>
         </para>
        </important>
 
+       <para>
+        <literal>NEXT VALUE FOR</literal> is the syntax specified by the SQL
+        standard.  All the function call variants are PostgreSQL extensions.
+       </para>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml
index c959146..bcb5aa8 100644
--- a/doc/src/sgml/ref/create_sequence.sgml
+++ b/doc/src/sgml/ref/create_sequence.sgml
@@ -49,11 +49,12 @@ <title>Description</title>
   </para>
 
   <para>
-   After a sequence is created, you use the functions
+   After a sequence is created, you use the expression <literal>NEXT VALUE FOR</literal>
+   and the functions
    <function>nextval</function>,
    <function>currval</function>, and
    <function>setval</function>
-   to operate on the sequence.  These functions are documented in
+   to operate on the sequence.  These expressions and functions are documented in
    <xref linkend="functions-sequence">.
   </para>
 
@@ -310,6 +311,11 @@ <title>Examples</title>
 ---------
      101
 </programlisting>
+
+   Alternatively,
+<programlisting>
+SELECT NEXT VALUE FOR serial;
+</programlisting>
   </para>
 
   <para>
@@ -355,13 +361,6 @@ <title>Compatibility</title>
     </listitem>
     <listitem>
      <para>
-      Obtaining the next value is done using the <function>nextval()</>
-      function instead of the standard's <command>NEXT VALUE FOR</command>
-      expression.
-     </para>
-    </listitem>
-    <listitem>
-     <para>
       The <literal>OWNED BY</> clause is a <productname>PostgreSQL</>
       extension.
      </para>
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 04d7840..db31b5d 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1940,6 +1940,13 @@ find_expr_references_walker(Node *node,
 						   context->addrs);
 		/* fall through to examine arguments */
 	}
+	else if (IsA(node, NextValueExpr))
+	{
+		NextValueExpr	   *nve = (NextValueExpr *) node;
+
+		add_object_address(OCLASS_CLASS, nve->seqid, 0,
+						   context->addrs);
+	}
 
 	return expression_tree_walker(node, find_expr_references_walker,
 								  (void *) context);
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..7f04c7f 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -88,7 +88,6 @@ static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
 static SeqTableData *last_used_seq = NULL;
 
 static void fill_seq_with_data(Relation rel, HeapTuple tuple);
-static int64 nextval_internal(Oid relid);
 static Relation open_share_lock(SeqTable seq);
 static void create_seq_hashtable(void);
 static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
@@ -530,7 +529,7 @@ nextval_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(nextval_internal(relid));
 }
 
-static int64
+int64
 nextval_internal(Oid relid)
 {
 	SeqTable	elm;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index cbb76d1..0397ca7 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -41,6 +41,7 @@
 #include "access/tupconvert.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_type.h"
+#include "commands/sequence.h"
 #include "executor/execdebug.h"
 #include "executor/nodeSubplan.h"
 #include "funcapi.h"
@@ -181,6 +182,8 @@ static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
 						bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 					  bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalNextValueExpr(ExprState *exprstate, ExprContext *econtext,
+					  bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
 						 ExprContext *econtext,
 						 bool *isNull, ExprDoneCond *isDone);
@@ -4426,6 +4429,20 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 	return 0;					/* keep compiler quiet */
 }
 
+/*
+ * ExecEvalNextValueExpr
+ */
+static Datum
+ExecEvalNextValueExpr(ExprState *exprstate, ExprContext *econtext,
+					  bool *isNull, ExprDoneCond *isDone)
+{
+	NextValueExpr *nve = (NextValueExpr *) exprstate->expr;
+
+	*isNull = false;
+	if (isDone)
+		*isDone = ExprSingleResult;
+	return Int64GetDatum(nextval_internal(nve->seqid));
+}
 
 /*
  * ExecEvalExprSwitchContext
@@ -5160,6 +5177,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
 			state = (ExprState *) makeNode(ExprState);
 			state->evalfunc = ExecEvalCurrentOfExpr;
 			break;
+		case T_NextValueExpr:
+			state = (ExprState *) makeNode(ExprState);
+			state->evalfunc = (ExprStateEvalFunc) ExecEvalNextValueExpr;
+			break;
 		case T_TargetEntry:
 			{
 				TargetEntry *tle = (TargetEntry *) node;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 3244c76..14391c5 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1870,6 +1870,20 @@ _copyCurrentOfExpr(const CurrentOfExpr *from)
 }
 
 /*
+ * _copyNextValueExpr
+ */
+static NextValueExpr *
+_copyNextValueExpr(const NextValueExpr *from)
+{
+	NextValueExpr *newnode = makeNode(NextValueExpr);
+
+	COPY_SCALAR_FIELD(seqid);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+/*
  * _copyInferenceElem
  */
 static InferenceElem *
@@ -4154,6 +4168,20 @@ _copyAlterPolicyStmt(const AlterPolicyStmt *from)
 	return newnode;
 }
 
+/*
+ * _copyNextValueClause
+ */
+static NextValueClause *
+_copyNextValueClause(const NextValueClause *from)
+{
+	NextValueClause *newnode = makeNode(NextValueClause);
+
+	COPY_NODE_FIELD(sequence);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
 /* ****************************************************************
  *					pg_list.h copy functions
  * ****************************************************************
@@ -4546,6 +4574,9 @@ copyObject(const void *from)
 		case T_CurrentOfExpr:
 			retval = _copyCurrentOfExpr(from);
 			break;
+		case T_NextValueExpr:
+			retval = _copyNextValueExpr(from);
+			break;
 		case T_InferenceElem:
 			retval = _copyInferenceElem(from);
 			break;
@@ -5065,6 +5096,9 @@ copyObject(const void *from)
 		case T_RoleSpec:
 			retval = _copyRoleSpec(from);
 			break;
+		case T_NextValueClause:
+			retval = _copyNextValueClause(from);
+			break;
 
 			/*
 			 * MISCELLANEOUS NODES
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 1eb6799..42d34ac 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -702,6 +702,15 @@ _equalCurrentOfExpr(const CurrentOfExpr *a, const CurrentOfExpr *b)
 }
 
 static bool
+_equalNextValueExpr(const NextValueExpr *a, const NextValueExpr *b)
+{
+	COMPARE_SCALAR_FIELD(seqid);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
+static bool
 _equalInferenceElem(const InferenceElem *a, const InferenceElem *b)
 {
 	COMPARE_NODE_FIELD(expr);
@@ -2619,6 +2628,15 @@ _equalRoleSpec(const RoleSpec *a, const RoleSpec *b)
 	return true;
 }
 
+static bool
+_equalNextValueClause(const NextValueClause *a, const NextValueClause *b)
+{
+	COMPARE_NODE_FIELD(sequence);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
 /*
  * Stuff from pg_list.h
  */
@@ -2863,6 +2881,9 @@ equal(const void *a, const void *b)
 		case T_CurrentOfExpr:
 			retval = _equalCurrentOfExpr(a, b);
 			break;
+		case T_NextValueExpr:
+			retval = _equalNextValueExpr(a, b);
+			break;
 		case T_InferenceElem:
 			retval = _equalInferenceElem(a, b);
 			break;
@@ -3369,6 +3390,9 @@ equal(const void *a, const void *b)
 		case T_RoleSpec:
 			retval = _equalRoleSpec(a, b);
 			break;
+		case T_NextValueClause:
+			retval = _equalNextValueClause(a, b);
+			break;
 
 		default:
 			elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index cd39167..079519f 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -244,6 +244,9 @@ exprType(const Node *expr)
 		case T_CurrentOfExpr:
 			type = BOOLOID;
 			break;
+		case T_NextValueExpr:
+			type = INT8OID;
+			break;
 		case T_InferenceElem:
 			{
 				const InferenceElem *n = (const InferenceElem *) expr;
@@ -913,6 +916,9 @@ exprCollation(const Node *expr)
 		case T_CurrentOfExpr:
 			coll = InvalidOid;	/* result is always boolean */
 			break;
+		case T_NextValueExpr:
+			coll = InvalidOid;	/* result is always an integer type */
+			break;
 		case T_InferenceElem:
 			coll = exprCollation((Node *) ((const InferenceElem *) expr)->expr);
 			break;
@@ -1114,6 +1120,9 @@ exprSetCollation(Node *expr, Oid collation)
 		case T_CurrentOfExpr:
 			Assert(!OidIsValid(collation));		/* result is always boolean */
 			break;
+		case T_NextValueExpr:
+			Assert(!OidIsValid(collation));		/* result is always an integer type */
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			break;
@@ -1535,6 +1544,12 @@ exprLocation(const Node *expr)
 			/* just use nested expr's location */
 			loc = exprLocation((Node *) ((const InferenceElem *) expr)->expr);
 			break;
+		case T_NextValueClause:
+			loc = ((const NextValueClause *) expr)->location;
+			break;
+		case T_NextValueExpr:
+			loc = ((const NextValueExpr *) expr)->location;
+			break;
 		default:
 			/* for any other node type it's just unknown... */
 			loc = -1;
@@ -1859,6 +1874,7 @@ expression_tree_walker(Node *node,
 		case T_CaseTestExpr:
 		case T_SetToDefault:
 		case T_CurrentOfExpr:
+		case T_NextValueExpr:
 		case T_RangeTblRef:
 		case T_SortGroupClause:
 			/* primitive node types with no expression subnodes */
@@ -2433,6 +2449,7 @@ expression_tree_mutator(Node *node,
 		case T_CaseTestExpr:
 		case T_SetToDefault:
 		case T_CurrentOfExpr:
+		case T_NextValueExpr:
 		case T_RangeTblRef:
 		case T_SortGroupClause:
 			return (Node *) copyObject(node);
@@ -3205,6 +3222,7 @@ raw_expression_tree_walker(Node *node,
 		case T_ParamRef:
 		case T_A_Const:
 		case T_A_Star:
+		case T_NextValueClause:
 			/* primitive node types with no subnodes */
 			break;
 		case T_Alias:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index acaf4ea..adc1d87 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1506,6 +1506,15 @@ _outCurrentOfExpr(StringInfo str, const CurrentOfExpr *node)
 }
 
 static void
+_outNextValueExpr(StringInfo str, const NextValueExpr *node)
+{
+	WRITE_NODE_TYPE("NEXTVALUEEXPR");
+
+	WRITE_OID_FIELD(seqid);
+	WRITE_LOCATION_FIELD(location);
+}
+
+static void
 _outInferenceElem(StringInfo str, const InferenceElem *node)
 {
 	WRITE_NODE_TYPE("INFERENCEELEM");
@@ -3543,6 +3552,9 @@ outNode(StringInfo str, const void *obj)
 			case T_CurrentOfExpr:
 				_outCurrentOfExpr(str, obj);
 				break;
+			case T_NextValueExpr:
+				_outNextValueExpr(str, obj);
+				break;
 			case T_InferenceElem:
 				_outInferenceElem(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 94954dc..cfde1a8 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1159,6 +1159,20 @@ _readCurrentOfExpr(void)
 }
 
 /*
+ * _readNextValueExpr
+ */
+static NextValueExpr *
+_readNextValueExpr(void)
+{
+	READ_LOCALS(NextValueExpr);
+
+	READ_OID_FIELD(seqid);
+	READ_LOCATION_FIELD(location);
+
+	READ_DONE();
+}
+
+/*
  * _readInferenceElem
  */
 static InferenceElem *
@@ -2362,6 +2376,8 @@ parseNodeString(void)
 		return_value = _readSetToDefault();
 	else if (MATCH("CURRENTOFEXPR", 13))
 		return_value = _readCurrentOfExpr();
+	else if (MATCH("NEXTVALUEEXPR", 13))
+		return_value = _readNextValueExpr();
 	else if (MATCH("INFERENCEELEM", 13))
 		return_value = _readInferenceElem();
 	else if (MATCH("TARGETENTRY", 11))
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6a0f7b3..30f230d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -447,6 +447,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <list>	row explicit_row implicit_row type_list array_expr_list
 %type <node>	case_expr case_arg when_clause case_default
 %type <list>	when_clause_list
+%type <node>	next_value_expr
 %type <ival>	sub_type
 %type <node>	ctext_expr
 %type <value>	NumericOnly
@@ -12105,6 +12106,8 @@ c_expr:		columnref								{ $$ = $1; }
 				{ $$ = $1; }
 			| func_expr
 				{ $$ = $1; }
+			| next_value_expr
+				{ $$ = $1; }
 			| select_with_parens			%prec UMINUS
 				{
 					SubLink *n = makeNode(SubLink);
@@ -13247,6 +13250,15 @@ case_arg:	a_expr									{ $$ = $1; }
 			| /*EMPTY*/								{ $$ = NULL; }
 		;
 
+next_value_expr: NEXT VALUE_P FOR qualified_name
+				{
+					NextValueClause *n = makeNode(NextValueClause);
+					n->sequence = $4;
+					n->location = @1;
+					$$ = (Node *)n;
+				}
+		;
+
 columnref:	ColId
 				{
 					$$ = makeColumnRef($1, NIL, @1, yyscanner);
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index cead212..e0466d8 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -15,6 +15,7 @@
 
 #include "postgres.h"
 
+#include "catalog/pg_class.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
@@ -111,6 +112,7 @@ static Node *transformXmlExpr(ParseState *pstate, XmlExpr *x);
 static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
 static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
 static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
+static Node *transformNextValueClause(ParseState *pstate, NextValueClause *n);
 static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
 static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
 					 int location);
@@ -338,6 +340,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 			result = transformCurrentOfExpr(pstate, (CurrentOfExpr *) expr);
 			break;
 
+		case T_NextValueClause:
+			result = transformNextValueClause(pstate, (NextValueClause *) expr);
+			break;
+
 			/*
 			 * CaseTestExpr and SetToDefault don't require any processing;
 			 * they are only injected into parse trees in fully-formed state.
@@ -2456,6 +2462,23 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
 	return (Node *) cexpr;
 }
 
+static Node *
+transformNextValueClause(ParseState *pstate, NextValueClause *n)
+{
+	NextValueExpr *result;
+
+	result = makeNode(NextValueExpr);
+	result->seqid = RangeVarGetRelid(n->sequence, NoLock, false);
+	if (get_rel_relkind(result->seqid) != RELKIND_SEQUENCE)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("\"%s\" is not a sequence",
+						get_rel_name(result->seqid))));
+	result->location = n->location;
+
+	return (Node *) result;
+}
+
 /*
  * Construct a whole-row reference to represent the notation "relation.*".
  */
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index fc93063..f95f4f6 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1761,6 +1761,9 @@ FigureColnameInternal(Node *node, char **name)
 					return 2;
 			}
 			break;
+		case T_NextValueClause:
+			*name = ((NextValueClause *) node)->sequence->relname;
+			return 2;
 		case T_XmlExpr:
 			/* make SQL/XML functions act like a regular function */
 			switch (((XmlExpr *) node)->op)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index ec966c7..a14bee3 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -8156,6 +8156,15 @@ get_rule_expr(Node *node, deparse_context *context,
 			}
 			break;
 
+		case T_NextValueExpr:
+			{
+				NextValueExpr *nve = (NextValueExpr *) node;
+
+				appendStringInfo(buf, "NEXT VALUE FOR %s",
+								 generate_relation_name(nve->seqid, NIL));
+			}
+			break;
+
 		case T_InferenceElem:
 			{
 				InferenceElem *iexpr = (InferenceElem *) node;
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 8469d9f..96750b7 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2636,6 +2636,12 @@ psql_completion(const char *text, int start, int end)
 							"SHARE ROW EXCLUSIVE MODE",
 							"EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE");
 
+/* NEXT VALUE FOR */
+	else if (TailMatches2("NEXT", "VALUE"))
+		COMPLETE_WITH_CONST("FOR");
+	else if (TailMatches3("NEXT", "VALUE", "FOR"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
+
 /* NOTIFY --- can be inside EXPLAIN, RULE, etc */
 	else if (TailMatches1("NOTIFY"))
 		COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s'");
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..45c9126 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -64,6 +64,8 @@ typedef struct xl_seq_rec
 	/* SEQUENCE TUPLE DATA FOLLOWS AT THE END */
 } xl_seq_rec;
 
+extern int64 nextval_internal(Oid relid);
+
 extern Datum nextval(PG_FUNCTION_ARGS);
 extern Datum nextval_oid(PG_FUNCTION_ARGS);
 extern Datum currval_oid(PG_FUNCTION_ARGS);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..bf44a1b 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -180,6 +180,7 @@ typedef enum NodeTag
 	T_FromExpr,
 	T_OnConflictExpr,
 	T_IntoClause,
+	T_NextValueExpr,
 
 	/*
 	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
@@ -452,6 +453,7 @@ typedef enum NodeTag
 	T_OnConflictClause,
 	T_CommonTableExpr,
 	T_RoleSpec,
+	T_NextValueClause,
 
 	/*
 	 * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1481fff..34e1186 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -342,6 +342,16 @@ typedef struct FuncCall
 } FuncCall;
 
 /*
+ * NextValueClause - NEXT VALUE FOR
+ */
+typedef struct NextValueClause
+{
+	NodeTag		type;
+	RangeVar   *sequence;
+	int			location;		/* token location, or -1 if unknown */
+} NextValueClause;
+
+/*
  * A_Star - '*' representing all columns of a table or compound field
  *
  * This can appear within ColumnRef.fields, A_Indirection.indirection, and
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index df2d27d..10f2d06 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1237,6 +1237,16 @@ typedef struct InferenceElem
 	Oid			inferopclass;	/* OID of att opclass, or InvalidOid */
 } InferenceElem;
 
+/*
+ * NextValueExpr - NEXT VALUE FOR
+ */
+typedef struct NextValueExpr
+{
+	Expr		xpr;
+	Oid			seqid;
+	int			location;		/* token location, or -1 if unknown */
+} NextValueExpr;
+
 /*--------------------
  * TargetEntry -
  *	   a target entry (used in query target lists)
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 4ffbe92..62d6e20 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -83,10 +83,10 @@ SELECT nextval('serialTest2_f5_seq');
        2
 (1 row)
 
-SELECT nextval('serialTest2_f6_seq');
- nextval 
----------
-       2
+SELECT NEXT VALUE FOR serialTest2_f6_seq;
+ serialtest2_f6_seq 
+--------------------
+                  2
 (1 row)
 
 -- basic sequence operations using both text and oid references
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 98a2e7d..ebd12fe 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -55,7 +55,7 @@ CREATE TABLE serialTest2 (f1 text, f2 serial, f3 smallserial, f4 serial2,
 SELECT nextval('serialTest2_f3_seq');
 SELECT nextval('serialTest2_f4_seq');
 SELECT nextval('serialTest2_f5_seq');
-SELECT nextval('serialTest2_f6_seq');
+SELECT NEXT VALUE FOR serialTest2_f6_seq;
 
 -- basic sequence operations using both text and oid references
 CREATE SEQUENCE sequence_test;
-- 
2.9.3

