so 23. 2. 2019 v 4:50 odesÃlatel Chapman Flack <[email protected]>
napsal:
> The following review has been posted through the commitfest application:
> make installcheck-world: tested, passed
> Implements feature: tested, passed
> Spec compliant: not tested
> Documentation: tested, passed
>
> The latest patch provides the same functionality without growing the size
> of struct ExprEvalStep, and without using the presence/absence of
> args/variadic_args to distinguish the cases. It now uses the args field
> consistently, and distinguishes the cases with new op constants,
> IS_GREATEST_VARIADIC and IS_LEAST_VARIADIC, assigned at parse time. I
> concede Tom's points about the comparative wartiness of the former patch.
>
> I'll change to WoA, though, for a few loose ends:
>
> In transformMinMaxExpr:
> The assignment of funcname doesn't look right.
> Two new errors are elogs. If they can be caused by user input (I'm sure
> the second one can), should they not be ereports?
> In fact, I think the second one should copy the equivalent one from
> parse_func.c:
>
> > ereport(ERROR,
> > (errcode(ERRCODE_DATATYPE_MISMATCH),
> > errmsg("VARIADIC argument must be an array"),
> > parser_errposition(pstate,
> > exprLocation((Node *) llast(fargs)))));
>
> ... both for consistency of the message, and so (I assume) it can use the
> existing translations for that message string.
>
good idea, done
> I am not sure if there is a way for user input to trigger the first one.
> Perhaps it can stay an elog if not. In any case, s/to
> determinate/determine/.
>
It is +/- internal error and usually should not be touched - so there I
prefer elog. I fix message
>
> In EvalExecMinMax:
>
> + if (cmpresult > 0 &&
> + (operator == IS_LEAST || operator ==
> IS_LEAST_VARIADIC))
> + *op->resvalue = value;
> + else if (cmpresult < 0 &&
> + (operator == IS_GREATEST ||
> operator == IS_GREATEST_VARIADIC))
>
> would it make sense to just compute a boolean isleast before entering the
> loop, to get simply (cmpresult > 0 && isleast) or (cmpresult < 0 &&
> !isleast) inside the loop? I'm unsure whether to assume the compiler will
> see that opportunity.
>
I am have not strong opinion about it. Personally I dislike a two variables
- but any discussion is partially about premature optimization. What about
using macros?
> Regards,
> -Chap
>
> The new status of this patch is: Waiting on Author
>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 86ff4e5c9e..aa4e80cca7 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -12679,6 +12679,12 @@ SELECT NULLIF(value, '(none)') ...
</synopsis>
<synopsis>
<function>LEAST</function>(<replaceable>value</replaceable> <optional>, ...</optional>)
+</synopsis>
+<synopsis>
+<function>GREATEST</function>(VARIADIC <replaceable>anyarray</replaceable>)
+</synopsis>
+<synopsis>
+<function>LEAST</function>(VARIADIC <replaceable>anyarray</replaceable>)
</synopsis>
<para>
@@ -12697,6 +12703,11 @@ SELECT NULLIF(value, '(none)') ...
make them return NULL if any argument is NULL, rather than only when
all are NULL.
</para>
+
+ <para>
+ These functions supports passing parameters as a array after
+ <literal>VARIADIC</literal> keyword.
+ </para>
</sect2>
</sect1>
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index a018925d4e..8ac4628ec1 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2811,6 +2811,8 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
FunctionCallInfo fcinfo = op->d.minmax.fcinfo_data;
MinMaxOp operator = op->d.minmax.op;
int off;
+ ArrayIterator array_iterator = NULL;
+ int nelems;
/* set at initialization */
Assert(fcinfo->args[0].isnull == false);
@@ -2819,16 +2821,49 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
/* default to null result */
*op->resnull = true;
- for (off = 0; off < op->d.minmax.nelems; off++)
+ if (IsMinMaxVariadic(operator))
{
+ ArrayType *arr;
+
+ /* result is null, when variadic argument is NULL */
+ if (nulls[0])
+ return;
+
+ /* prepare iterarator */
+ arr = DatumGetArrayTypeP(values[0]);
+ array_iterator = array_create_iterator(arr, 0, NULL);
+
+ nelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+ }
+ else
+ nelems = op->d.minmax.nelems;
+
+ for (off = 0; off < nelems; off++)
+ {
+ Datum value;
+ bool isnull;
+
+ if (array_iterator)
+ {
+ bool has_data PG_USED_FOR_ASSERTS_ONLY;
+
+ has_data = array_iterate(array_iterator, &value, &isnull);
+ Assert(has_data);
+ }
+ else
+ {
+ value = values[off];
+ isnull = nulls[off];
+ }
+
/* ignore NULL inputs */
- if (nulls[off])
+ if (isnull)
continue;
if (*op->resnull)
{
/* first nonnull input, adopt value */
- *op->resvalue = values[off];
+ *op->resvalue = value;
*op->resnull = false;
}
else
@@ -2837,19 +2872,23 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
/* apply comparison function */
fcinfo->args[0].value = *op->resvalue;
- fcinfo->args[1].value = values[off];
+ fcinfo->args[1].value = value;
fcinfo->isnull = false;
cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
if (fcinfo->isnull) /* probably should not happen */
continue;
- if (cmpresult > 0 && operator == IS_LEAST)
- *op->resvalue = values[off];
- else if (cmpresult < 0 && operator == IS_GREATEST)
- *op->resvalue = values[off];
+ if (cmpresult > 0 && IsMinMaxLeast(operator))
+ *op->resvalue = value;
+ else if (cmpresult < 0 && IsMinMaxGreatest(operator))
+ *op->resvalue = value;
}
}
+
+ /* release iterator */
+ if (array_iterator)
+ array_free_iterator(array_iterator);
}
/*
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 8ed30c011a..f68cebfee8 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -465,7 +465,8 @@ exprTypmod(const Node *expr)
int32 typmod;
ListCell *arg;
- if (exprType((Node *) linitial(mexpr->args)) != minmaxtype)
+ if (!IsMinMaxVariadic(mexpr->op) &&
+ exprType((Node *) linitial(mexpr->args)) != minmaxtype)
return -1;
typmod = exprTypmod((Node *) linitial(mexpr->args));
if (typmod < 0)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a68f78e0e0..65f44a8df6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -13803,6 +13803,22 @@ func_expr_common_subexpr:
v->location = @1;
$$ = (Node *)v;
}
+ | GREATEST '(' VARIADIC a_expr ')'
+ {
+ MinMaxExpr *v = makeNode(MinMaxExpr);
+ v->args = list_make1($4);
+ v->op = IS_GREATEST_VARIADIC;
+ v->location = @1;
+ $$ = (Node *)v;
+ }
+ | LEAST '(' VARIADIC a_expr ')'
+ {
+ MinMaxExpr *v = makeNode(MinMaxExpr);
+ v->args = list_make1($4);
+ v->op = IS_LEAST_VARIADIC;
+ v->location = @1;
+ $$ = (Node *)v;
+ }
| XMLCONCAT '(' expr_list ')'
{
$$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1);
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index e559353529..45aae3f89b 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2276,22 +2276,47 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m)
newargs = lappend(newargs, newe);
}
- newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL);
- /* minmaxcollid and inputcollid will be set by parse_collate.c */
+ if (IsMinMaxVariadic(newm->op))
+ {
+ Oid array_typid;
+ Oid element_typid;
- /* Convert arguments if necessary */
- foreach(args, newargs)
+ array_typid = exprType(linitial(newargs));
+
+ if (array_typid == InvalidOid)
+ elog(ERROR, "cannot to determine result type");
+
+ element_typid = get_base_element_type(array_typid);
+ if (!OidIsValid(element_typid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("VARIADIC argument must be an array"),
+ parser_errposition(pstate,
+ exprLocation((Node *) linitial(newargs)))));
+
+ newm->minmaxtype = element_typid;
+ newm->args = newargs;
+ }
+ else
{
- Node *e = (Node *) lfirst(args);
- Node *newe;
+ newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL);
+ /* minmaxcollid and inputcollid will be set by parse_collate.c */
- newe = coerce_to_common_type(pstate, e,
- newm->minmaxtype,
- funcname);
- newcoercedargs = lappend(newcoercedargs, newe);
+ /* Convert arguments if necessary */
+ foreach(args, newargs)
+ {
+ Node *e = (Node *) lfirst(args);
+ Node *newe;
+
+ newe = coerce_to_common_type(pstate, e,
+ newm->minmaxtype,
+ funcname);
+ newcoercedargs = lappend(newcoercedargs, newe);
+ }
+
+ newm->args = newcoercedargs;
}
- newm->args = newcoercedargs;
newm->location = m->location;
return (Node *) newm;
}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 0e9598ebfe..de1794201a 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1839,15 +1839,20 @@ FigureColnameInternal(Node *node, char **name)
*name = "coalesce";
return 2;
case T_MinMaxExpr:
- /* make greatest/least act like a regular function */
- switch (((MinMaxExpr *) node)->op)
{
- case IS_GREATEST:
+ /* make greatest/least act like a regular function */
+ MinMaxOp op = ((MinMaxExpr *) node)->op;
+
+ if (IsMinMaxGreatest(op))
+ {
*name = "greatest";
return 2;
- case IS_LEAST:
+ }
+ else if (IsMinMaxLeast(op))
+ {
*name = "least";
return 2;
+ }
}
break;
case T_SQLValueFunction:
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 1258092dc8..b5a58403ee 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -8465,16 +8465,19 @@ get_rule_expr(Node *node, deparse_context *context,
{
MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
- switch (minmaxexpr->op)
+ if (IsMinMaxGreatest(minmaxexpr->op))
+ appendStringInfoString(buf, "GREATEST(");
+ else if (IsMinMaxLeast(minmaxexpr->op))
+ appendStringInfoString(buf, "LEAST(");
+
+ if (IsMinMaxVariadic(minmaxexpr->op))
{
- case IS_GREATEST:
- appendStringInfoString(buf, "GREATEST(");
- break;
- case IS_LEAST:
- appendStringInfoString(buf, "LEAST(");
- break;
+ appendStringInfoString(buf, "VARIADIC ");
+ get_rule_expr(linitial(minmaxexpr->args), context, true);
}
- get_rule_expr((Node *) minmaxexpr->args, context, true);
+ else
+ get_rule_expr((Node *) minmaxexpr->args, context, true);
+
appendStringInfoChar(buf, ')');
}
break;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index a7efae7038..d3c10f5dbd 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1080,9 +1080,18 @@ typedef struct CoalesceExpr
typedef enum MinMaxOp
{
IS_GREATEST,
- IS_LEAST
+ IS_LEAST,
+ IS_GREATEST_VARIADIC,
+ IS_LEAST_VARIADIC
} MinMaxOp;
+#define IsMinMaxVariadic(op) (op == IS_GREATEST_VARIADIC || \
+ op == IS_LEAST_VARIADIC)
+#define IsMinMaxLeast(op) (op == IS_LEAST || \
+ op == IS_LEAST_VARIADIC)
+#define IsMinMaxGreatest(op) (op == IS_GREATEST || \
+ op == IS_GREATEST_VARIADIC)
+
typedef struct MinMaxExpr
{
Expr xpr;
diff --git a/src/test/regress/expected/case.out b/src/test/regress/expected/case.out
index c0c8acf035..eca127ed2d 100644
--- a/src/test/regress/expected/case.out
+++ b/src/test/regress/expected/case.out
@@ -392,3 +392,54 @@ ROLLBACK;
--
DROP TABLE CASE_TBL;
DROP TABLE CASE2_TBL;
+--
+-- greatest and least tests
+--
+SELECT least(10,20,30,40);
+ least
+-------
+ 10
+(1 row)
+
+SELECT greatest(10,20,30,40);
+ greatest
+----------
+ 40
+(1 row)
+
+SELECT least(VARIADIC ARRAY[10,20,30,40]);
+ least
+-------
+ 10
+(1 row)
+
+SELECT greatest(VARIADIC ARRAY[10,20,30,40]);
+ greatest
+----------
+ 40
+(1 row)
+
+SELECT least(10,20,30,40, NULL);
+ least
+-------
+ 10
+(1 row)
+
+SELECT greatest(10,20,30,40, NULL);
+ greatest
+----------
+ 40
+(1 row)
+
+SELECT least(VARIADIC ARRAY[10,20,30,40, NULL]);
+ least
+-------
+ 10
+(1 row)
+
+SELECT greatest(VARIADIC ARRAY[10,20,30,40, NULL]);
+ greatest
+----------
+ 40
+(1 row)
+
diff --git a/src/test/regress/sql/case.sql b/src/test/regress/sql/case.sql
index 17436c524a..c482676e17 100644
--- a/src/test/regress/sql/case.sql
+++ b/src/test/regress/sql/case.sql
@@ -252,3 +252,16 @@ ROLLBACK;
DROP TABLE CASE_TBL;
DROP TABLE CASE2_TBL;
+
+--
+-- greatest and least tests
+--
+SELECT least(10,20,30,40);
+SELECT greatest(10,20,30,40);
+SELECT least(VARIADIC ARRAY[10,20,30,40]);
+SELECT greatest(VARIADIC ARRAY[10,20,30,40]);
+
+SELECT least(10,20,30,40, NULL);
+SELECT greatest(10,20,30,40, NULL);
+SELECT least(VARIADIC ARRAY[10,20,30,40, NULL]);
+SELECT greatest(VARIADIC ARRAY[10,20,30,40, NULL]);