Hi We can pass variadic arguments as a array to any variadic function. But some our old variadic functions doesn't supports this feature.
We cannot to write SELECT least(VARIADIC ARRAY[1,2,3]); Attached patch add this possibility to least, greatest functions. Regards Pavel
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 885da18306..53021b1ae2 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -1852,13 +1852,18 @@ ExecInitExprRec(Expr *node, ExprState *state, case T_MinMaxExpr: { MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; - int nelems = list_length(minmaxexpr->args); + int nelems; TypeCacheEntry *typentry; FmgrInfo *finfo; FunctionCallInfo fcinfo; ListCell *lc; int off; + if (minmaxexpr->args) + nelems = list_length(minmaxexpr->args); + else + nelems = 1; + /* Look up the btree comparison function for the datatype */ typentry = lookup_type_cache(minmaxexpr->minmaxtype, TYPECACHE_CMP_PROC); @@ -1895,16 +1900,29 @@ ExecInitExprRec(Expr *node, ExprState *state, scratch.d.minmax.finfo = finfo; scratch.d.minmax.fcinfo_data = fcinfo; - /* evaluate expressions into minmax->values/nulls */ - off = 0; - foreach(lc, minmaxexpr->args) + if (minmaxexpr->args) { - Expr *e = (Expr *) lfirst(lc); + scratch.d.minmax.variadic_arg = false; - ExecInitExprRec(e, state, - &scratch.d.minmax.values[off], - &scratch.d.minmax.nulls[off]); - off++; + /* evaluate expressions into minmax->values/nulls */ + off = 0; + foreach(lc, minmaxexpr->args) + { + Expr *e = (Expr *) lfirst(lc); + + ExecInitExprRec(e, state, + &scratch.d.minmax.values[off], + &scratch.d.minmax.nulls[off]); + off++; + } + } + else + { + scratch.d.minmax.variadic_arg = true; + + ExecInitExprRec(minmaxexpr->variadic_arg, state, + &scratch.d.minmax.values[0], + &scratch.d.minmax.nulls[0]); } /* and push the final comparison */ diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c08df6f162..e26487d315 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2791,6 +2791,53 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op) /* default to null result */ *op->resnull = true; + if (op->d.minmax.variadic_arg) + { + ArrayIterator array_iterator; + ArrayType *arr; + Datum value; + bool isnull; + + if (nulls[0]) + return; + + arr = DatumGetArrayTypeP(values[0]); + + array_iterator = array_create_iterator(arr, 0, NULL); + while (array_iterate(array_iterator, &value, &isnull)) + { + if (isnull) + continue; + + if (*op->resnull) + { + /* first nonnull input */ + *op->resvalue = value; + *op->resnull = false; + } + else + { + int cmpresult; + + /* apply comparison function */ + fcinfo->arg[0] = *op->resvalue; + fcinfo->arg[1] = value; + + fcinfo->isnull = false; + cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo)); + if (fcinfo->isnull) /* probably should not happen */ + continue; + + if (cmpresult > 0 && operator == IS_LEAST) + *op->resvalue = value; + else if (cmpresult < 0 && operator == IS_GREATEST) + *op->resvalue = value; + } + } + + return; + } + for (off = 0; off < op->d.minmax.nelems; off++) { /* ignore NULL inputs */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e8ea59e34a..3df2d42ad6 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1942,6 +1942,7 @@ _copyMinMaxExpr(const MinMaxExpr *from) COPY_SCALAR_FIELD(inputcollid); COPY_SCALAR_FIELD(op); COPY_NODE_FIELD(args); + COPY_NODE_FIELD(variadic_arg); COPY_LOCATION_FIELD(location); return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 3bb91c9595..1c6fddac2a 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -634,6 +634,7 @@ _equalMinMaxExpr(const MinMaxExpr *a, const MinMaxExpr *b) COMPARE_SCALAR_FIELD(inputcollid); COMPARE_SCALAR_FIELD(op); COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(variadic_arg); COMPARE_LOCATION_FIELD(location); return true; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index a10014f755..2fbdcd78f8 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -465,6 +465,9 @@ exprTypmod(const Node *expr) int32 typmod; ListCell *arg; + if (mexpr->variadic_arg) + return exprTypmod((Node *) mexpr->variadic_arg); + if (exprType((Node *) linitial(mexpr->args)) != minmaxtype) return -1; typmod = exprTypmod((Node *) linitial(mexpr->args)); @@ -2065,7 +2068,15 @@ expression_tree_walker(Node *node, case T_CoalesceExpr: return walker(((CoalesceExpr *) node)->args, context); case T_MinMaxExpr: - return walker(((MinMaxExpr *) node)->args, context); + { + MinMaxExpr *mexpr = (MinMaxExpr *) node; + + if (walker(mexpr->args, context)) + return; + if (walker(mexpr->variadic_arg, context)) + return true; + } + break; case T_XmlExpr: { XmlExpr *xexpr = (XmlExpr *) node; @@ -2811,6 +2822,7 @@ expression_tree_mutator(Node *node, FLATCOPY(newnode, minmaxexpr, MinMaxExpr); MUTATE(newnode->args, minmaxexpr->args, List *); + MUTATE(newnode->variadic_arg, minmaxexpr->variadic_arg, Expr *); return (Node *) newnode; } break; @@ -3329,7 +3341,15 @@ raw_expression_tree_walker(Node *node, case T_CoalesceExpr: return walker(((CoalesceExpr *) node)->args, context); case T_MinMaxExpr: - return walker(((MinMaxExpr *) node)->args, context); + { + MinMaxExpr *mexpr = (MinMaxExpr *) node; + + if (walker(mexpr->args, context)) + return true; + if (walker(mexpr->variadic_arg, context)) + return true; + } + break; case T_XmlExpr: { XmlExpr *xexpr = (XmlExpr *) node; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 69731ccdea..0eddbd1a3b 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1597,6 +1597,7 @@ _outMinMaxExpr(StringInfo str, const MinMaxExpr *node) WRITE_OID_FIELD(inputcollid); WRITE_ENUM_FIELD(op, MinMaxOp); WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(variadic_arg); WRITE_LOCATION_FIELD(location); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e117867de5..ca9ef21136 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1086,6 +1086,7 @@ _readMinMaxExpr(void) READ_OID_FIELD(inputcollid); READ_ENUM_FIELD(op, MinMaxOp); READ_NODE_FIELD(args); + READ_NODE_FIELD(variadic_arg); READ_LOCATION_FIELD(location); READ_DONE(); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 6d23bfb0b3..99bacaa2fc 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -13848,6 +13848,22 @@ func_expr_common_subexpr: v->location = @1; $$ = (Node *)v; } + | GREATEST '(' VARIADIC a_expr ')' + { + MinMaxExpr *v = makeNode(MinMaxExpr); + v->variadic_arg = $4; + v->op = IS_GREATEST; + v->location = @1; + $$ = (Node *)v; + } + | LEAST '(' VARIADIC a_expr ')' + { + MinMaxExpr *v = makeNode(MinMaxExpr); + v->variadic_arg = $4; + v->op = IS_LEAST; + 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 385e54a9b6..2520d62610 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2258,35 +2258,60 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) MinMaxExpr *newm = makeNode(MinMaxExpr); List *newargs = NIL; List *newcoercedargs = NIL; - const char *funcname = (m->op == IS_GREATEST) ? "GREATEST" : "LEAST"; - ListCell *args; newm->op = m->op; - foreach(args, m->args) + + if (m->args) { - Node *e = (Node *) lfirst(args); - Node *newe; + ListCell *args; + const char *funcname = (m->op == IS_GREATEST) ? "GREATEST" : "LEAST"; - newe = transformExprRecurse(pstate, e); - newargs = lappend(newargs, newe); - } + foreach(args, m->args) + { + 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 = transformExprRecurse(pstate, e); + newargs = lappend(newargs, newe); + } - /* Convert arguments if necessary */ - foreach(args, newargs) + newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL); + /* minmaxcollid and inputcollid will be set by parse_collate.c */ + + /* 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; + } + else { - Node *e = (Node *) lfirst(args); - Node *newe; + Oid input_type; + Oid element_typeid; - newe = coerce_to_common_type(pstate, e, - newm->minmaxtype, - funcname); - newcoercedargs = lappend(newcoercedargs, newe); + Assert(m->variadic_arg); + + newm->variadic_arg = transformExprRecurse(pstate, (Node *) m->variadic_arg); + input_type = exprType(newm->variadic_arg); + + if (input_type == InvalidOid) + elog(ERROR, "cannot to determinate result type"); + + element_typeid = get_base_element_type(input_type); + if (!OidIsValid(element_typeid)) + elog(ERROR, "expected a array parameter"); + + newm->minmaxtype = element_typeid; } - newm->args = newcoercedargs; newm->location = m->location; return (Node *) newm; } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 29884f1c8b..8e56829345 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8443,7 +8443,17 @@ get_rule_expr(Node *node, deparse_context *context, appendStringInfoString(buf, "LEAST("); break; } - get_rule_expr((Node *) minmaxexpr->args, context, true); + + if (minmaxexpr->args) + { + get_rule_expr((Node *) minmaxexpr->args, context, true); + } + else + { + appendStringInfoString(buf, "VARIADIC "); + get_rule_expr((Node *) minmaxexpr->variadic_arg, context, true); + } + appendStringInfoChar(buf, ')'); } break; diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 02af68f2c2..7debc2e598 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -453,6 +453,7 @@ typedef struct ExprEvalStep /* workspace for argument values */ Datum *values; bool *nulls; + bool variadic_arg; int nelems; /* is it GREATEST or LEAST? */ MinMaxOp op; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index b886ed3534..acbb7226c1 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1087,6 +1087,7 @@ typedef struct MinMaxExpr Oid inputcollid; /* OID of collation that function should use */ MinMaxOp op; /* function to execute */ List *args; /* the arguments */ + Expr *variadic_arg; /* the variadic argument */ int location; /* token location, or -1 if unknown */ } MinMaxExpr;