Hi
pá 16. 8. 2019 v 8:41 odesílatel Pavel Stehule <[email protected]>
napsal:
> Hi
>
> rebase
>
another rebase
Regards
Pavel
> Pavel
>
diff --git a/contrib/Makefile b/contrib/Makefile
index 92184ed487..dafa42844d 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -15,6 +15,7 @@ SUBDIRS = \
citext \
cube \
dblink \
+ decode \
dict_int \
dict_xsyn \
earthdistance \
diff --git a/contrib/decode/.gitignore b/contrib/decode/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/contrib/decode/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/contrib/decode/Makefile b/contrib/decode/Makefile
new file mode 100644
index 0000000000..bb29732c61
--- /dev/null
+++ b/contrib/decode/Makefile
@@ -0,0 +1,20 @@
+# contrib/decode/Makefile
+
+MODULES = decode
+
+EXTENSION = decode
+DATA = decode--1.0.sql
+PGFILEDESC = "decode - example of parser support function"
+
+REGRESS = decode
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/decode
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/decode/decode--1.0.sql b/contrib/decode/decode--1.0.sql
new file mode 100644
index 0000000000..34408809ff
--- /dev/null
+++ b/contrib/decode/decode--1.0.sql
@@ -0,0 +1,26 @@
+/* contrib/decode/decode--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION decode" to load this file. \quit
+
+--
+-- PostgreSQL code for decode.
+--
+
+--
+-- Parser support function - allow to specify returning type when
+-- system with polymorphic variables is possible to use.
+--
+CREATE FUNCTION decode_support(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- decode function - example of function that returns "any" type
+--
+CREATE FUNCTION decode(variadic "any")
+RETURNS "any"
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE SUPPORT decode_support;
+
diff --git a/contrib/decode/decode.c b/contrib/decode/decode.c
new file mode 100644
index 0000000000..32919bf846
--- /dev/null
+++ b/contrib/decode/decode.c
@@ -0,0 +1,459 @@
+/*
+ * contrib/decode/decode.c
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "catalog/pg_type.h"
+#include "nodes/supportnodes.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_oper.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(decode_support);
+PG_FUNCTION_INFO_V1(decode);
+
+static void decode_detect_types(int nargs, Oid *argtypes, Oid *search_oid, Oid *result_oid);
+static Oid select_common_type_from_vector(int nargs, Oid *typeids, bool noerror);
+
+typedef struct decode_cache
+{
+ Oid rettype;
+ int nargs;
+ Oid *argtypes;
+ Oid *target_types;
+ Oid search_typid;
+ Oid result_typid;
+ CoercionPathType *ctype;
+ FmgrInfo *cast_finfo;
+ FmgrInfo *input_finfo;
+ Oid *typioparam;
+ FmgrInfo eqop_finfo;
+} decode_cache;
+
+static void
+free_cache(decode_cache *cache)
+{
+ pfree(cache->argtypes);
+ pfree(cache->target_types);
+ pfree(cache->ctype);
+ pfree(cache->cast_finfo);
+ pfree(cache->input_finfo);
+ pfree(cache->typioparam);
+ pfree(cache);
+}
+
+/*
+ * prepare persistent cache used for fast internal parameter casting
+ */
+static decode_cache *
+build_cache(int nargs,
+ Oid *argtypes,
+ MemoryContext target_ctx)
+{
+ Oid search_typid;
+ Oid result_typid;
+ Oid eqop;
+ decode_cache *cache;
+ MemoryContext oldctx;
+ int i;
+
+ oldctx = MemoryContextSwitchTo(target_ctx);
+
+ cache = palloc(sizeof(decode_cache));
+
+ cache->argtypes = palloc(nargs * sizeof(Oid));
+ cache->target_types = palloc(nargs * sizeof(Oid));
+ cache->ctype = palloc(nargs * sizeof(CoercionPathType));
+ cache->cast_finfo = palloc(nargs * sizeof(FmgrInfo));
+ cache->input_finfo = palloc(nargs * sizeof(FmgrInfo));
+ cache->typioparam = palloc(nargs * sizeof(Oid));
+
+ MemoryContextSwitchTo(oldctx);
+
+ decode_detect_types(nargs, argtypes, &search_typid, &result_typid);
+
+ cache->search_typid = search_typid;
+ cache->result_typid = result_typid;
+
+ for (i = 0; i < nargs; i++)
+ {
+ Oid src_typid;
+ Oid target_typid;
+
+ src_typid = cache->argtypes[i] = argtypes[i];
+
+ if (i == 0)
+ target_typid = search_typid;
+ else if (i % 2) /* even position */
+ {
+ if (i + 1 < nargs)
+ target_typid = search_typid;
+ else
+ /* last even argument is a default value */
+ target_typid = result_typid;
+ }
+ else /* odd position */
+ target_typid = result_typid;
+
+ cache->target_types[i] = target_typid;
+
+ /* prepare cast if it is necessary */
+ if (src_typid != target_typid)
+ {
+ Oid funcid;
+
+ cache->ctype[i] = find_coercion_pathway(target_typid, src_typid,
+ COERCION_ASSIGNMENT, &funcid);
+ if (cache->ctype[i] == COERCION_PATH_NONE)
+ /* A previously detected cast is not available now */
+ elog(ERROR, "could not find cast from %u to %u",
+ src_typid, target_typid);
+
+ if (cache->ctype[i] != COERCION_PATH_RELABELTYPE)
+ {
+ if (cache->ctype[i] == COERCION_PATH_FUNC)
+ {
+ fmgr_info(funcid, &cache->cast_finfo[i]);
+ }
+ else
+ {
+ Oid outfuncoid;
+ Oid infunc;
+ bool typisvarlena;
+
+ getTypeOutputInfo(src_typid, &outfuncoid, &typisvarlena);
+ fmgr_info(outfuncoid, &cache->cast_finfo[i]);
+
+ getTypeInputInfo(target_typid, &infunc, &cache->typioparam[i]);
+ fmgr_info(infunc, &cache->input_finfo[i]);
+ }
+ }
+ }
+ }
+
+ get_sort_group_operators(search_typid, false, true, false, NULL, &eqop, NULL, NULL);
+ fmgr_info(get_opcode(eqop), &cache->eqop_finfo);
+
+ return cache;
+}
+
+/*
+ * Returns converted value into target type
+ */
+static Datum
+decode_cast(decode_cache *cache, int argn, Datum value)
+{
+ Datum result;
+
+ if (cache->argtypes[argn] != cache->target_types[argn])
+ {
+ if (cache->ctype[argn] == COERCION_PATH_RELABELTYPE)
+ result = value;
+ else if (cache->ctype[argn] == COERCION_PATH_FUNC)
+ result = FunctionCall1(&cache->cast_finfo[argn], value);
+ else
+ {
+ char *str;
+
+ str = OutputFunctionCall(&cache->cast_finfo[argn], value);
+ result = InputFunctionCall(&cache->input_finfo[argn],
+ str,
+ cache->typioparam[argn],
+ -1);
+ }
+ }
+ else
+ result = value;
+
+ return result;
+}
+
+/*
+ * Returns true, if cache can be used again
+ */
+static bool
+is_valid_cache(int nargs, Oid *argtypes, decode_cache *cache)
+{
+ if (cache)
+ {
+ if (nargs == cache->nargs)
+ {
+ int i;
+
+ for (i = 0; i < nargs; i++)
+ if (argtypes[i] != cache->argtypes[i])
+ return false;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void
+decode_detect_types(int nargs,
+ Oid *argtypes,
+ Oid *search_oid, /* result */
+ Oid *result_oid) /* result */
+{
+ Oid search_typids[FUNC_MAX_ARGS];
+ Oid result_typids[FUNC_MAX_ARGS];
+ int search_nargs = 0;
+ int result_nargs = 0;
+
+ Assert(nargs >= 3);
+
+ *search_oid = argtypes[1] != UNKNOWNOID ? argtypes[1] : TEXTOID;
+ *result_oid = argtypes[2] != UNKNOWNOID ? argtypes[2] : TEXTOID;
+
+ /* Search most common type if target type is not a text */
+ if (*search_oid != TEXTOID || *result_oid != TEXTOID)
+ {
+ int i;
+
+ for (i = 0; i < nargs; i++)
+ {
+ if (i == 0)
+ search_typids[search_nargs++] = argtypes[0];
+ else if (i % 2) /* even position */
+ {
+ if (i + 1 < nargs)
+ search_typids[search_nargs++] = argtypes[i];
+ else
+ result_typids[result_nargs++] = argtypes[i];
+ }
+ else /* odd position */
+ result_typids[result_nargs++] = argtypes[i];
+ }
+
+ if (*search_oid != TEXTOID)
+ {
+ *search_oid = select_common_type_from_vector(search_nargs,
+ search_typids,
+ true);
+
+ if (!OidIsValid(*search_oid)) /* should not to be */
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot to detect common type for search expression")));
+ }
+
+ if (*result_oid != TEXTOID)
+ {
+ *result_oid = select_common_type_from_vector(result_nargs,
+ result_typids,
+ true);
+
+ if (!OidIsValid(*result_oid)) /* should not to be */
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot to detect common type for result expression")));
+ }
+ }
+}
+
+
+Datum
+decode_support(PG_FUNCTION_ARGS)
+{
+ Node *rawreq = (Node *) PG_GETARG_POINTER(0);
+ Node *ret = NULL;
+
+ if (IsA(rawreq, SupportRequestRettype))
+ {
+ SupportRequestRettype *req = (SupportRequestRettype *) rawreq;
+ Oid search_oid;
+ Oid result_oid;
+
+ if (req->nargs < 3)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("too few function arguments"),
+ errhint("The decode function requires at least 3 arguments")));
+
+ decode_detect_types(req->nargs, req->actual_arg_types, &search_oid, &result_oid);
+
+ req->rettype = result_oid;
+
+ ret = (Node *) req;
+ }
+
+ PG_RETURN_POINTER(ret);
+}
+
+Datum
+decode(PG_FUNCTION_ARGS)
+{
+ Datum expr = (Datum) 0;
+ bool expr_isnull;
+ Oid argtypes[FUNC_MAX_ARGS];
+ Oid collation;
+ decode_cache *cache;
+ int nargs = PG_NARGS();
+ int result_argn;
+ int i;
+
+ if (nargs < 3)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("too few function arguments"),
+ errhint("The decode function requires at least 3 arguments")));
+
+ /* collect arg types */
+ for (i = 0; i < nargs; i++)
+ argtypes[i] = get_fn_expr_argtype(fcinfo->flinfo, i);
+
+ cache = (decode_cache *) fcinfo->flinfo->fn_extra;
+ if (!is_valid_cache(nargs, argtypes, cache))
+ {
+ if (cache)
+ free_cache(cache);
+ cache = build_cache(nargs, argtypes, fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = cache;
+ }
+
+ /* recheck rettype, should not be */
+ if (get_fn_expr_rettype(fcinfo->flinfo) != cache->result_typid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("function has unexpected result type %d", get_fn_expr_rettype(fcinfo->flinfo)),
+ errhint("The decode expects \"%s\" type",
+ format_type_be(cache->result_typid))));
+
+ /* try to set result_argn to position with default arrgument */
+ result_argn = nargs % 2 ? - 1 : nargs - 1;
+
+ expr_isnull = PG_ARGISNULL(0);
+ collation = PG_GET_COLLATION();
+
+ if (!expr_isnull)
+ expr = decode_cast(cache, 0, PG_GETARG_DATUM(0));
+
+ for (i = 1; i < nargs; i+= 2)
+ {
+ if (!expr_isnull && !PG_ARGISNULL(i) && i + 1 < nargs)
+ {
+ Datum eqop_result;
+ Datum value;
+
+ value = decode_cast(cache, i, PG_GETARG_DATUM(i));
+ eqop_result = FunctionCall2Coll(&cache->eqop_finfo, collation, expr, value);
+
+ if (DatumGetBool(eqop_result))
+ {
+ result_argn = i + 1;
+ break;
+ }
+ }
+ else if (expr_isnull && PG_ARGISNULL(i))
+ {
+ result_argn = i + 1;
+ break;
+ }
+ }
+
+ if (result_argn >= 0 && !PG_ARGISNULL(result_argn))
+ PG_RETURN_DATUM(decode_cast(cache, result_argn, PG_GETARG_DATUM(result_argn)));
+
+ PG_RETURN_NULL();
+}
+
+/*
+ * select_common_type_from_vector()
+ * Determine the common supertype of vector of Oids.
+ *
+ * Similar to select_common_type() but simplified for polymorphics
+ * type processing. When there are no supertype, then returns InvalidOid,
+ * when noerror is true, or raise exception when noerror is false.
+ */
+static Oid
+select_common_type_from_vector(int nargs, Oid *typeids, bool noerror)
+{
+ int i = 0;
+ Oid ptype;
+ TYPCATEGORY pcategory;
+ bool pispreferred;
+
+ Assert(nargs > 0);
+ ptype = typeids[0];
+
+ /* fast leave when all types are same */
+ if (ptype != UNKNOWNOID)
+ {
+ for (i = 1; i < nargs; i++)
+ {
+ if (ptype != typeids[i])
+ break;
+ }
+
+ if (i == nargs)
+ return ptype;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm. Note that at this point, lc
+ * points to the first list item with type different from pexpr's; we need
+ * not re-examine any items the previous loop advanced over.
+ */
+ ptype = getBaseType(ptype);
+ get_type_category_preferred(ptype, &pcategory, &pispreferred);
+
+ for (; i < nargs; i++)
+ {
+ Oid ntype = getBaseType(typeids[i]);
+
+ /* move on to next one if no new information... */
+ if (ntype != UNKNOWNOID && ntype != ptype)
+ {
+ TYPCATEGORY ncategory;
+ bool nispreferred;
+
+ get_type_category_preferred(ntype, &ncategory, &nispreferred);
+
+ if (ptype == UNKNOWNOID)
+ {
+ /* so far, only unknowns so take anything... */
+ ptype = ntype;
+ pcategory = ncategory;
+ pispreferred = nispreferred;
+ }
+ else if (ncategory != pcategory)
+ {
+ if (noerror)
+ return InvalidOid;
+
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("types %s and %s cannot be matched",
+ format_type_be(ptype),
+ format_type_be(ntype))));
+ }
+ else if (!pispreferred &&
+ can_coerce_type(1, &ptype, &ntype, COERCION_IMPLICIT) &&
+ !can_coerce_type(1, &ntype, &ptype, COERCION_IMPLICIT))
+ {
+ /*
+ * take new type if can coerce to it implicitly but not the
+ * other way; but if we have a preferred type, stay on it.
+ */
+ ptype = ntype;
+ pcategory = ncategory;
+ pispreferred = nispreferred;
+ }
+ }
+ }
+
+ /*
+ * Be consistent with select_common_type()
+ */
+ if (ptype == UNKNOWNOID)
+ ptype = TEXTOID;
+
+ return ptype;
+}
diff --git a/contrib/decode/decode.control b/contrib/decode/decode.control
new file mode 100644
index 0000000000..048499d4ab
--- /dev/null
+++ b/contrib/decode/decode.control
@@ -0,0 +1,5 @@
+# decode extension
+comment = 'decode - demo of function with parser support function'
+default_version = '1.0'
+module_pathname = '$libdir/decode'
+relocatable = true
diff --git a/contrib/decode/expected/decode.out b/contrib/decode/expected/decode.out
new file mode 100644
index 0000000000..dfb9edbdbc
--- /dev/null
+++ b/contrib/decode/expected/decode.out
@@ -0,0 +1,40 @@
+--
+-- Test decode function
+--
+CREATE EXTENSION decode;
+SELECT decode(1, 1, 5, 0);
+ decode
+--------
+ 5
+(1 row)
+
+SELECT decode(2, 1, 5, 0);
+ decode
+--------
+ 0
+(1 row)
+
+SELECT decode(1, 1, 5, 0.5);
+ decode
+--------
+ 5
+(1 row)
+
+SELECT decode(2, 1, 5, 0.5);
+ decode
+--------
+ 0.5
+(1 row)
+
+SELECT decode(1, 1, 'Ahoj', 'Nazdar');
+ decode
+--------
+ Ahoj
+(1 row)
+
+SELECT decode(2, 1, 'Ahoj', 'Nazdar');
+ decode
+--------
+ Nazdar
+(1 row)
+
diff --git a/contrib/decode/sql/decode.sql b/contrib/decode/sql/decode.sql
new file mode 100644
index 0000000000..ee93ad058c
--- /dev/null
+++ b/contrib/decode/sql/decode.sql
@@ -0,0 +1,13 @@
+--
+-- Test decode function
+--
+
+CREATE EXTENSION decode;
+
+SELECT decode(1, 1, 5, 0);
+SELECT decode(2, 1, 5, 0);
+SELECT decode(1, 1, 5, 0.5);
+SELECT decode(2, 1, 5, 0.5);
+
+SELECT decode(1, 1, 'Ahoj', 'Nazdar');
+SELECT decode(2, 1, 'Ahoj', 'Nazdar');
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 1ac235a0f4..fe0f440353 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -845,6 +845,7 @@ lookup_agg_function(List *fnName,
int nvargs;
Oid vatype;
Oid *true_oid_array;
+ Oid support_func;
FuncDetailCode fdresult;
AclResult aclresult;
int i;
@@ -860,7 +861,8 @@ lookup_agg_function(List *fnName,
nargs, input_types, false, false,
&fnOid, rettype, &retset,
&nvargs, &vatype,
- &true_oid_array, NULL);
+ &true_oid_array, NULL,
+ &support_func);
/* only valid case is a normal function not returning a set */
if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index d9c6dc1901..978ceb1290 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -22,6 +22,7 @@
#include "lib/stringinfo.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "nodes/supportnodes.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
@@ -111,6 +112,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool retset;
int nvargs;
Oid vatype;
+ Oid support_func;
FuncDetailCode fdresult;
char aggkind = 0;
ParseCallbackState pcbstate;
@@ -265,7 +267,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
!func_variadic, true,
&funcid, &rettype, &retset,
&nvargs, &vatype,
- &declared_arg_types, &argdefaults);
+ &declared_arg_types, &argdefaults,
+ &support_func);
cancel_parser_errposition_callback(&pcbstate);
@@ -666,6 +669,31 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
+ /*
+ * When rettype is ANYOID we can call support function SupportRequestRettype if
+ * it is available to get real type.
+ */
+ if (rettype == ANYOID && OidIsValid(support_func))
+ {
+ SupportRequestRettype req;
+ SupportRequestRettype *result;
+
+ req.type = T_SupportRequestRettype;
+ req.funcname = funcname;
+ req.fargs = fargs;
+ req.actual_arg_types = actual_arg_types;
+ req.declared_arg_types = declared_arg_types;
+ req.nargs = nargsplusdefs;
+
+ result = (SupportRequestRettype *)
+ DatumGetPointer(OidFunctionCall1(support_func,
+ PointerGetDatum(&req)));
+
+ /* use result when it is valid */
+ if (result == &req)
+ rettype = result->rettype;
+ }
+
/*
* If the function isn't actually variadic, forget any VARIADIC decoration
* on the call. (Perhaps we should throw an error instead, but
@@ -1392,7 +1420,8 @@ func_get_detail(List *funcname,
int *nvargs, /* return value */
Oid *vatype, /* return value */
Oid **true_typeids, /* return value */
- List **argdefaults) /* optional return value */
+ List **argdefaults, /* optional return value */
+ Oid *support_func) /* return value */
{
FuncCandidateList raw_candidates;
FuncCandidateList best_candidate;
@@ -1404,6 +1433,7 @@ func_get_detail(List *funcname,
*nvargs = 0;
*vatype = InvalidOid;
*true_typeids = NULL;
+ *support_func = InvalidOid;
if (argdefaults)
*argdefaults = NIL;
@@ -1518,6 +1548,7 @@ func_get_detail(List *funcname,
*nvargs = 0;
*vatype = InvalidOid;
*true_typeids = argtypes;
+ *support_func = InvalidOid;
return FUNCDETAIL_COERCION;
}
}
@@ -1615,6 +1646,7 @@ func_get_detail(List *funcname,
*rettype = pform->prorettype;
*retset = pform->proretset;
*vatype = pform->provariadic;
+ *support_func = pform->prosupport;
/* fetch default args if caller wants 'em */
if (argdefaults && best_candidate->ndargs > 0)
{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 13685a0a0e..c4347b037b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10880,6 +10880,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
int p_nvargs;
Oid p_vatype;
Oid *p_true_typeids;
+ Oid p_support_func;
bool force_qualify = false;
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
@@ -10934,7 +10935,8 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
!use_variadic, true,
&p_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_vatype,
- &p_true_typeids, NULL);
+ &p_true_typeids, NULL,
+ &p_support_func);
else
{
p_result = FUNCDETAIL_NOTFOUND;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index bce2d59b0d..0e636d95e6 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -513,7 +513,8 @@ typedef enum NodeTag
T_SupportRequestSelectivity, /* in nodes/supportnodes.h */
T_SupportRequestCost, /* in nodes/supportnodes.h */
T_SupportRequestRows, /* in nodes/supportnodes.h */
- T_SupportRequestIndexCondition /* in nodes/supportnodes.h */
+ T_SupportRequestIndexCondition, /* in nodes/supportnodes.h */
+ T_SupportRequestRettype /* in nodes/supportnodes.h */
} NodeTag;
/*
diff --git a/src/include/nodes/supportnodes.h b/src/include/nodes/supportnodes.h
index 460d75bd2d..abaf42cf96 100644
--- a/src/include/nodes/supportnodes.h
+++ b/src/include/nodes/supportnodes.h
@@ -239,4 +239,25 @@ typedef struct SupportRequestIndexCondition
* equivalent of the function call */
} SupportRequestIndexCondition;
+/*
+ * The SupportRequestRettype request type allows to calculate result type for
+ * functions that returns "any" type. It is designed for procedural specification
+ * return type.
+ */
+typedef struct SupportRequestRettype
+{
+ NodeTag type;
+
+ /* Input fields */
+ List *funcname;
+ List *fargs;
+ Oid *actual_arg_types;
+ Oid *declared_arg_types;
+ int nargs;
+
+ /* Output fields */
+ Oid rettype;
+
+} SupportRequestRettype;
+
#endif /* SUPPORTNODES_H */
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 5a3b287eaf..7fc2ef1d37 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -41,7 +41,8 @@ extern FuncDetailCode func_get_detail(List *funcname,
bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype,
bool *retset, int *nvargs, Oid *vatype,
- Oid **true_typeids, List **argdefaults);
+ Oid **true_typeids, List **argdefaults,
+ Oid *support_func);
extern int func_match_argtypes(int nargs,
Oid *input_typeids,