Here is a patch for implementing the NEXT VALUE FOR expression. This is the SQL-standard conforming version of our nextval() function, and it's also used by Oracle, MS SQL, DB2. Example:
SELECT NEXT VALUE FOR foo_seq; The second patch changes the serial column to use this new expression for its generated default values. This doesn't make an external difference except perhaps that the generated expression looks less weird to the user. -- Peter Eisentraut http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From c012df68bdfc8711d142f7a91f8ea0ee4ecef813 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@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
From 8a793af8ff0d62a93b8e2ee3b0bcf7377ca6c97e Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Sun, 14 Aug 2016 12:00:00 -0400 Subject: [PATCH 2/2] Use NEXT VALUE FOR for serial columns In the expansion of the default value generated by serial columns, use the NEXT VALUE FOR expression instead of the nextval function. There is no behavior change, but the expression becomes simpler and less ugly. --- doc/src/sgml/datatype.sgml | 4 ++-- src/backend/parser/parse_utilcmd.c | 30 ++++++++------------------ src/test/regress/expected/replica_identity.out | 30 +++++++++++++------------- 3 files changed, 26 insertions(+), 38 deletions(-) diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 67d0c34..a99071b 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -850,7 +850,7 @@ <title>Serial Types</title> <programlisting> CREATE SEQUENCE <replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq; CREATE TABLE <replaceable class="parameter">tablename</replaceable> ( - <replaceable class="parameter">colname</replaceable> integer NOT NULL DEFAULT nextval('<replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq') + <replaceable class="parameter">colname</replaceable> integer NOT NULL DEFAULT NEXT VALUE FOR <replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq ); ALTER SEQUENCE <replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq OWNED BY <replaceable class="parameter">tablename</replaceable>.<replaceable class="parameter">colname</replaceable>; </programlisting> @@ -874,7 +874,7 @@ <title>Serial Types</title> from the sequence is still "used up" even if a row containing that value is never successfully inserted into the table column. This may happen, for example, if the inserting transaction rolls back. - See <literal>nextval()</literal> in <xref linkend="functions-sequence"> + See <literal>NEXT VALUE FOR</literal> in <xref linkend="functions-sequence"> for details. </para> </note> diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index e98fad0..bb088b5 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -411,10 +411,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) Oid snamespaceid; char *snamespace; char *sname; - char *qstring; - A_Const *snamenode; - TypeCast *castnode; - FuncCall *funccallnode; + NextValueClause *nvcnode; CreateSeqStmt *seqstmt; AlterSeqStmt *altseqstmt; List *attnamelist; @@ -491,27 +488,18 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) * rather than shortcutting, so that we will detect any conflicting * constraints the user wrote (like a different DEFAULT). * - * Create an expression tree representing the function call - * nextval('sequencename'). We cannot reduce the raw tree to cooked - * form until after the sequence is created, but there's no need to do - * so. + * Create an expression tree representing NEXT VALUE FOR + * "sequencename". We cannot reduce the raw tree to cooked form until + * after the sequence is created, but there's no need to do so. */ - qstring = quote_qualified_identifier(snamespace, sname); - snamenode = makeNode(A_Const); - snamenode->val.type = T_String; - snamenode->val.val.str = qstring; - snamenode->location = -1; - castnode = makeNode(TypeCast); - castnode->typeName = SystemTypeName("regclass"); - castnode->arg = (Node *) snamenode; - castnode->location = -1; - funccallnode = makeFuncCall(SystemFuncName("nextval"), - list_make1(castnode), - -1); + nvcnode = makeNode(NextValueClause); + nvcnode->sequence = seqstmt->sequence; + nvcnode->location = -1; + constraint = makeNode(Constraint); constraint->contype = CONSTR_DEFAULT; constraint->location = -1; - constraint->raw_expr = (Node *) funccallnode; + constraint->raw_expr = (Node *) nvcnode; constraint->cooked_expr = NULL; column->constraints = lappend(column->constraints, constraint); diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index 39a60a5..2efb86f 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -77,10 +77,10 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; (1 row) \d test_replica_identity - Table "public.test_replica_identity" - Column | Type | Modifiers ---------+---------+-------------------------------------------------------------------- - id | integer | not null default nextval('test_replica_identity_id_seq'::regclass) + Table "public.test_replica_identity" + Column | Type | Modifiers +--------+---------+-------------------------------------------------------------- + id | integer | not null default NEXT VALUE FOR test_replica_identity_id_seq keya | text | not null keyb | text | not null nonkey | text | @@ -110,10 +110,10 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; (1 row) \d test_replica_identity - Table "public.test_replica_identity" - Column | Type | Modifiers ---------+---------+-------------------------------------------------------------------- - id | integer | not null default nextval('test_replica_identity_id_seq'::regclass) + Table "public.test_replica_identity" + Column | Type | Modifiers +--------+---------+-------------------------------------------------------------- + id | integer | not null default NEXT VALUE FOR test_replica_identity_id_seq keya | text | not null keyb | text | not null nonkey | text | @@ -159,13 +159,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; (1 row) \d+ test_replica_identity - Table "public.test_replica_identity" - Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+--------------------------------------------------------------------+----------+--------------+------------- - id | integer | not null default nextval('test_replica_identity_id_seq'::regclass) | plain | | - keya | text | not null | extended | | - keyb | text | not null | extended | | - nonkey | text | | extended | | + Table "public.test_replica_identity" + Column | Type | Modifiers | Storage | Stats target | Description +--------+---------+--------------------------------------------------------------+----------+--------------+------------- + id | integer | not null default NEXT VALUE FOR test_replica_identity_id_seq | plain | | + keya | text | not null | extended | | + keyb | text | not null | extended | | + nonkey | text | | extended | | Indexes: "test_replica_identity_pkey" PRIMARY KEY, btree (id) "test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3)) -- 2.9.3
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers