Hi pá 26. 7. 2019 v 22:53 odesílatel Tom Lane <t...@sss.pgh.pa.us> napsal:
> I wrote: > > TBH, I don't like this proposal one bit. As far as I can see, the idea > > is to let a function's support function redefine the function's declared > > argument and result types on-the-fly according to no predetermined rules, > > and that seems to me like it's a recipe for disaster. How will anyone > > understand which function(s) are candidates to match a query, or why one > > particular candidate got selected over others? It's already hard enough > > to understand the behavior of polymorphic functions in complex cases, > > and those are much more constrained than this would be. > > After thinking about this a bit more, it seems like you could avoid > a lot of problems if you restricted what the support function call > does to be potentially replacing the result type of a function > declared to return ANY with some more-specific type (computed from > examination of the actual arguments). That would make it act much > more like a traditional polymorphic function. It'd remove the issues > about interactions among multiple potentially-matching functions, > since we'd only call a single support function for an already-identified > target function. > > You'd still need to touch everyplace that knows about polymorphic > type resolution, since this would essentially be another form of > polymorphic function. And I'm still very dubious that it's worth > the trouble. But it would be a lot more controllable than the > proposal as it stands. > I am sending reduced version of previous patch. Now, support function is used just for replacement of returned type "any" by some other. The are two patches - shorter with only support function, larger with demo "decode" function. I don't expect so the "decode" extension should be pushed to master. It is just demo of usage. Regards Pavel > regards, tom lane >
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 201242e796..640c506dd5 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 8e926539e6..5974744668 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; @@ -1407,6 +1436,7 @@ func_get_detail(List *funcname, *nvargs = 0; *vatype = InvalidOid; *true_typeids = NULL; + *support_func = InvalidOid; if (argdefaults) *argdefaults = NIL; @@ -1519,6 +1549,7 @@ func_get_detail(List *funcname, *nvargs = 0; *vatype = InvalidOid; *true_typeids = argtypes; + *support_func = InvalidOid; return FUNCDETAIL_COERCION; } } @@ -1616,6 +1647,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 13d5d542f9..a406f9c699 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -10868,6 +10868,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)); @@ -10922,7 +10923,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 4e2fb39105..e2fb1cb83a 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -512,7 +512,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,
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 201242e796..640c506dd5 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 8e926539e6..5974744668 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; @@ -1407,6 +1436,7 @@ func_get_detail(List *funcname, *nvargs = 0; *vatype = InvalidOid; *true_typeids = NULL; + *support_func = InvalidOid; if (argdefaults) *argdefaults = NIL; @@ -1519,6 +1549,7 @@ func_get_detail(List *funcname, *nvargs = 0; *vatype = InvalidOid; *true_typeids = argtypes; + *support_func = InvalidOid; return FUNCDETAIL_COERCION; } } @@ -1616,6 +1647,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 13d5d542f9..a406f9c699 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -10868,6 +10868,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)); @@ -10922,7 +10923,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 4e2fb39105..e2fb1cb83a 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -512,7 +512,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,