diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index b9d4a936905..fa409d72b7b 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -2763,6 +2763,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node) APP_JUMB(ce->cursor_param); } break; + case T_NextValueExpr: + { + NextValueExpr *nve = (NextValueExpr *) node; + + APP_JUMB(nve->seqid); + APP_JUMB(nve->typeId); + } + break; case T_InferenceElem: { InferenceElem *ie = (InferenceElem *) node; diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 1a7645d341f..6fffc290fad 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1791,6 +1791,13 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, cd->resulttype, 0, context->addrs); } + else if (IsA(node, NextValueExpr)) + { + NextValueExpr *nve = (NextValueExpr *) node; + + add_object_address(OCLASS_CLASS, nve->seqid, 0, + context->addrs); + } else if (IsA(node, OnConflictExpr)) { OnConflictExpr *onconflict = (OnConflictExpr *) node; @@ -1942,13 +1949,6 @@ 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/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c227d9bdd99..f2a52f62135 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -1232,21 +1232,11 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) EEO_CASE(EEOP_NEXTVALUEEXPR) { - switch (op->d.nextvalueexpr.seqtypid) - { - case INT2OID: - *op->resvalue = Int16GetDatum((int16) nextval_internal(op->d.nextvalueexpr.seqid, false)); - break; - case INT4OID: - *op->resvalue = Int32GetDatum((int32) nextval_internal(op->d.nextvalueexpr.seqid, false)); - break; - case INT8OID: - *op->resvalue = Int64GetDatum((int64) nextval_internal(op->d.nextvalueexpr.seqid, false)); - break; - default: - elog(ERROR, "unsupported sequence type %u", op->d.nextvalueexpr.seqtypid); - } - *op->resnull = false; + /* + * Doesn't seem worthwhile to have an inline implementation + * efficiency-wise. + */ + ExecEvalNextValueExpr(state, op); EEO_NEXT(); } @@ -1989,6 +1979,32 @@ ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op) errmsg("WHERE CURRENT OF is not supported for this table type"))); } +/* + * Evaluate NextValueExpr. + */ +void +ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op) +{ + int64 newval = nextval_internal(op->d.nextvalueexpr.seqid, false); + + switch (op->d.nextvalueexpr.seqtypid) + { + case INT2OID: + *op->resvalue = Int16GetDatum((int16) newval); + break; + case INT4OID: + *op->resvalue = Int32GetDatum((int32) newval); + break; + case INT8OID: + *op->resvalue = Int64GetDatum((int64) newval); + break; + default: + elog(ERROR, "unsupported sequence type %u", + op->d.nextvalueexpr.seqtypid); + } + *op->resnull = false; +} + /* * Evaluate NullTest / IS NULL for rows. */ diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 97ba25fc72c..e3eb0c57887 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -1642,10 +1642,10 @@ set_sa_opfuncid(ScalarArrayOpExpr *opexpr) * for themselves, in case additional checks should be made, or because they * have special rules about which parts of the tree need to be visited. * - * Note: we ignore MinMaxExpr, SQLValueFunction, XmlExpr, and CoerceToDomain - * nodes, because they do not contain SQL function OIDs. However, they can - * invoke SQL-visible functions, so callers should take thought about how to - * treat them. + * Note: we ignore MinMaxExpr, SQLValueFunction, XmlExpr, CoerceToDomain, + * and NextValueExpr nodes, because they do not contain SQL function OIDs. + * However, they can invoke SQL-visible functions, so callers should take + * thought about how to treat them. */ bool check_functions_in_node(Node *node, check_function_callback checker, @@ -1865,12 +1865,12 @@ expression_tree_walker(Node *node, case T_Var: case T_Const: case T_Param: - case T_CoerceToDomainValue: case T_CaseTestExpr: + case T_SQLValueFunction: + case T_CoerceToDomainValue: case T_SetToDefault: case T_CurrentOfExpr: case T_NextValueExpr: - case T_SQLValueFunction: case T_RangeTblRef: case T_SortGroupClause: /* primitive node types with no expression subnodes */ @@ -2461,12 +2461,12 @@ expression_tree_mutator(Node *node, } break; case T_Param: - case T_CoerceToDomainValue: case T_CaseTestExpr: + case T_SQLValueFunction: + case T_CoerceToDomainValue: case T_SetToDefault: case T_CurrentOfExpr: case T_NextValueExpr: - case T_SQLValueFunction: case T_RangeTblRef: case T_SortGroupClause: return (Node *) copyObject(node); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b0abe9ec10f..21e39a0b810 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1610,6 +1610,15 @@ _outCurrentOfExpr(StringInfo str, const CurrentOfExpr *node) WRITE_INT_FIELD(cursor_param); } +static void +_outNextValueExpr(StringInfo str, const NextValueExpr *node) +{ + WRITE_NODE_TYPE("NEXTVALUEEXPR"); + + WRITE_OID_FIELD(seqid); + WRITE_OID_FIELD(typeId); +} + static void _outInferenceElem(StringInfo str, const InferenceElem *node) { @@ -3872,6 +3881,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 1380703cbc6..8ab09d74d60 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1202,6 +1202,20 @@ _readCurrentOfExpr(void) READ_DONE(); } +/* + * _readNextValueExpr + */ +static NextValueExpr * +_readNextValueExpr(void) +{ + READ_LOCALS(NextValueExpr); + + READ_OID_FIELD(seqid); + READ_OID_FIELD(typeId); + + READ_DONE(); +} + /* * _readInferenceElem */ @@ -2517,6 +2531,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/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index eb653cf3bec..b35acb7bdcf 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -3627,6 +3627,15 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) cpu_operator_cost; } } + else if (IsA(node, MinMaxExpr) || + IsA(node, SQLValueFunction) || + IsA(node, XmlExpr) || + IsA(node, CoerceToDomain) || + IsA(node, NextValueExpr)) + { + /* Treat all these as having cost 1 */ + context->total.per_tuple += cpu_operator_cost; + } else if (IsA(node, CurrentOfExpr)) { /* Report high cost to prevent selection of anything but TID scan */ diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 8961ed88a81..8b4425dcf90 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -902,6 +902,12 @@ contain_mutable_functions_walker(Node *node, void *context) return true; } + if (IsA(node, NextValueExpr)) + { + /* NextValueExpr is volatile */ + return true; + } + /* * It should be safe to treat MinMaxExpr as immutable, because it will * depend on a non-cross-type btree comparison function, and those should @@ -969,6 +975,12 @@ contain_volatile_functions_walker(Node *node, void *context) context)) return true; + if (IsA(node, NextValueExpr)) + { + /* NextValueExpr is volatile */ + return true; + } + /* * See notes in contain_mutable_functions_walker about why we treat * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable, while @@ -1019,6 +1031,8 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context) * See notes in contain_mutable_functions_walker about why we treat * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable, while * SQLValueFunction is stable. Hence, none of them are of interest here. + * Also, since we're intentionally ignoring nextval(), presumably we + * should ignore NextValueExpr. */ /* Recurse to check arguments */ @@ -1146,7 +1160,7 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context) * contain a parallel-unsafe function; but useful constraints probably * never would have such, and assuming they do would cripple use of * parallel query in the presence of domain types.) SQLValueFunction - * should be safe in all cases. + * should be safe in all cases. NextValueExpr is parallel-unsafe. */ if (IsA(node, CoerceToDomain)) { @@ -1154,6 +1168,12 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context) return true; } + if (IsA(node, NextValueExpr)) + { + if (max_parallel_hazard_test(PROPARALLEL_UNSAFE, context)) + return true; + } + /* * As a notational convenience for callers, look through RestrictInfo. */ @@ -1495,6 +1515,7 @@ contain_leaked_vars_walker(Node *node, void *context) case T_SQLValueFunction: case T_NullTest: case T_BooleanTest: + case T_NextValueExpr: case T_List: /* diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 5cfb916684f..d2fb20d5649 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -7283,6 +7283,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_MinMaxExpr: case T_SQLValueFunction: case T_XmlExpr: + case T_NextValueExpr: case T_NullIfExpr: case T_Aggref: case T_WindowFunc: @@ -8612,6 +8613,22 @@ get_rule_expr(Node *node, deparse_context *context, } break; + case T_NextValueExpr: + { + NextValueExpr *nvexpr = (NextValueExpr *) node; + + /* + * This isn't exactly nextval(), but that seems close enough + * for EXPLAIN's purposes. + */ + appendStringInfoString(buf, "nextval("); + simple_quote_literal(buf, + generate_relation_name(nvexpr->seqid, + NIL)); + appendStringInfoChar(buf, ')'); + } + break; + case T_InferenceElem: { InferenceElem *iexpr = (InferenceElem *) node; diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 7a65339f01a..8ee0496e010 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -362,7 +362,7 @@ typedef struct ExprEvalStep SQLValueFunction *svf; } sqlvaluefunction; - /* for EEOP_NEXTVALUEXPR */ + /* for EEOP_NEXTVALUEEXPR */ struct { Oid seqid; @@ -615,6 +615,7 @@ extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext); extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op); extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op); +extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op); extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext); extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op, diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 01527399b80..27bd4f3363e 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -183,6 +183,7 @@ typedef enum NodeTag T_CoerceToDomainValue, T_SetToDefault, T_CurrentOfExpr, + T_NextValueExpr, T_InferenceElem, T_TargetEntry, T_RangeTblRef, @@ -190,7 +191,6 @@ typedef enum NodeTag T_FromExpr, T_OnConflictExpr, T_IntoClause, - T_NextValueExpr, /* * TAGS FOR EXPRESSION STATE NODES (execnodes.h) diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 38015ed5405..8c536a8d38d 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1279,6 +1279,20 @@ typedef struct CurrentOfExpr int cursor_param; /* refcursor parameter number, or 0 */ } CurrentOfExpr; +/* + * NextValueExpr - get next value from sequence + * + * This has the same effect as calling the nextval() function, but it does not + * check permissions on the sequence. This is used for identity columns, + * where the sequence is an implicit dependency without its own permissions. + */ +typedef struct NextValueExpr +{ + Expr xpr; + Oid seqid; + Oid typeId; +} NextValueExpr; + /* * InferenceElem - an element of a unique index inference specification * @@ -1294,20 +1308,6 @@ typedef struct InferenceElem Oid inferopclass; /* OID of att opclass, or InvalidOid */ } InferenceElem; -/* - * NextValueExpr - get next value from sequence - * - * This has the same effect as calling the nextval() function, but it does not - * check permissions on the sequence. This is used for identity columns, - * where the sequence is an implicit dependency without its own permissions. - */ -typedef struct NextValueExpr -{ - Expr xpr; - Oid seqid; - Oid typeId; -} NextValueExpr; - /*-------------------- * TargetEntry - * a target entry (used in query target lists)