Hi so 9. 3. 2019 v 7:22 odesÃlatel Pavel Stehule <pavel.steh...@gmail.com> napsal:
> Hi, > > Tom introduced supported functions for calculation function's selectivity. > Still I have similar idea to use supported function for calculation > function's parameter's types and function return type. > > Motivation: > > Reduce a necessity of overloading of functions. My motivation is related > primary to Orafce, but this feature should be helpful for anybody with > similar goals. The function's overloading is great functionality but it is > hard for maintenance. > > My idea to enhance a CREATE FUNCTION command to be able do > > CREATE FUCNTION foo("any") > RETURNS "any" AS ... > TYPEINFO foo_typeinfo > > CREATE FUNCTION decode(VARIADIC "any") > RETURNS "any" AS ... > TYPEINFO decode_typeinfo. > > The typeinfo functions returns a pointer tu structure with param types and > result type. Only function with "any" parameters or "any" result can use > TYPEINFO supported function. This functionality should not be allowed for > common functions. > > This functionality is limited just for C coders. But I expect so typical > application coder doesn't need it. It doesn't replace my proposal of > introduction other polymorphic type - now named "commontype" (can be named > differently). The commontype is good enough solution for application > coders, developers. > > Comments, notes? > here is a patch I have not a plan to push decode function to upstream. Patch contains it just as demo. Regards Pavel > > Regards > > Pavel > > >
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index cdd5006a72..79fce58cd9 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -1084,6 +1084,8 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames, effective_nargs * sizeof(Oid)); newResult->pathpos = pathpos; newResult->oid = procform->oid; + newResult->support_func = InvalidOid; + newResult->rettype = procform->prorettype; newResult->nargs = effective_nargs; newResult->argnumbers = argnumbers; if (argnumbers) @@ -1100,6 +1102,7 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames, /* Simple positional case, just copy proargtypes as-is */ memcpy(newResult->args, procform->proargtypes.values, pronargs * sizeof(Oid)); + } if (variadic) { @@ -1114,6 +1117,25 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames, newResult->nvargs = 0; newResult->ndargs = use_defaults ? pronargs - nargs : 0; + /* + * When there are support function, we should to detect, if + * there is any "any" argument, and if there is, then we should + * to set support_tp_func_oid field. + */ + if (OidIsValid(procform->prosupport)) + { + int i; + + for (i = 0; i < pronargs; i++) + { + if (newResult->args[i] == ANYOID) + { + newResult->support_func = procform->prosupport; + break; + } + } + } + /* * Does it have the same arguments as something we already accepted? * If so, decide what to do to avoid returning duplicate argument @@ -1701,6 +1723,8 @@ OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok) newResult->pathpos = pathpos; newResult->oid = operform->oid; + newResult->support_func = InvalidOid; + newResult->rettype = operform->oprresult; newResult->nargs = 2; newResult->nvargs = 0; newResult->ndargs = 0; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 752cf1b315..46989604e3 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" @@ -927,6 +928,36 @@ func_match_argtypes(int nargs, current_candidate = next_candidate) { next_candidate = current_candidate->next; + + if (OidIsValid(current_candidate->support_func)) + { + SupportRequestArgTypes req; + SupportRequestArgTypes *result; + + req.type = T_SupportRequestArgTypes; + req.candidate = current_candidate; + req.nargs = nargs; + req.typeids = input_typeids; + + result = (SupportRequestArgTypes *) + DatumGetPointer(OidFunctionCall1(current_candidate->support_func, + PointerGetDatum(&req))); + + /* + * Support function should not to support this request. Then + * do nothing. Support function can detect failure, and returns + * null as candidate. Then eliminate this candidate from list. + */ + if (result == &req) + { + if (result->candidate) + current_candidate = result->candidate; + else + /* skip this candidate */ + continue; + } + } + if (can_coerce_type(nargs, input_typeids, current_candidate->args, COERCION_IMPLICIT)) { @@ -1589,6 +1620,7 @@ func_get_detail(List *funcname, *funcid = best_candidate->oid; *nvargs = best_candidate->nvargs; + *rettype = best_candidate->rettype; *true_typeids = best_candidate->args; /* @@ -1617,7 +1649,6 @@ func_get_detail(List *funcname, elog(ERROR, "cache lookup failed for function %u", best_candidate->oid); pform = (Form_pg_proc) GETSTRUCT(ftup); - *rettype = pform->prorettype; *retset = pform->proretset; *vatype = pform->provariadic; /* fetch default args if caller wants 'em */ diff --git a/src/backend/utils/adt/oracle_compat.c b/src/backend/utils/adt/oracle_compat.c index 9d01e0be9c..18002e41d1 100644 --- a/src/backend/utils/adt/oracle_compat.c +++ b/src/backend/utils/adt/oracle_compat.c @@ -15,9 +15,16 @@ */ #include "postgres.h" +#include "nodes/nodes.h" +#include "nodes/supportnodes.h" +#include "catalog/namespace.h" +#include "catalog/pg_type.h" #include "common/int.h" +#include "parser/parse_coerce.h" +#include "parser/parse_oper.h" #include "utils/builtins.h" #include "utils/formatting.h" +#include "utils/lsyscache.h" #include "mb/pg_wchar.h" @@ -1066,3 +1073,337 @@ repeat(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(result); } + +typedef struct decode_fmgr_cache +{ + Oid typid; + FmgrInfo eqop_finfo; +} decode_fmgr_cache; + +/* + * Decode function + */ +Datum +decode(PG_FUNCTION_ARGS) +{ + decode_fmgr_cache *fmgr_cache; + Oid search_oid; + Oid result_oid; + Oid rettype; + Oid collation; + int i; + int result_narg; + int nargs = PG_NARGS(); + bool expr_isnull; + + if (nargs < 3) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("too few function arguments"), + errhint("The decode function requires at least 3 arguments"))); + + search_oid = get_fn_expr_argtype(fcinfo->flinfo, 1); + result_oid = get_fn_expr_argtype(fcinfo->flinfo, 2); + collation = PG_GET_COLLATION(); + + if (fcinfo->flinfo->fn_extra == NULL) + { + MemoryContext oldcxt; + Oid eqop; + + get_sort_group_operators(search_oid, false, true, false, NULL, &eqop, NULL, NULL); + + oldcxt = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); + + fmgr_cache = palloc(sizeof(decode_fmgr_cache)); + fmgr_cache->typid = search_oid; + fmgr_info(get_opcode(eqop), &fmgr_cache->eqop_finfo); + + MemoryContextSwitchTo(oldcxt); + + fcinfo->flinfo->fn_extra = fmgr_cache; + } + else + fmgr_cache = fcinfo->flinfo->fn_extra; + + /* recheck of fmgr_cache validity, should not be */ + if (fmgr_cache->typid != search_oid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("search expression has unexpected type"), + errhint("The decode expects \"%s\" type", + format_type_be(search_oid)))); + + /* recheck rerttype, should not be */ + rettype = get_fn_expr_rettype(fcinfo->flinfo); + if (rettype != result_oid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("result expression has unexpected type"), + errhint("The decode expects \"%s\" type", + format_type_be(result_oid)))); + + result_narg = nargs % 2 ? -1 : nargs - 1; + expr_isnull = PG_ARGISNULL(0); + + for (i = 1; i < nargs; i += 2) + { + if (expr_isnull) + { + if (PG_ARGISNULL(i) && i + 1 < nargs) + { + result_narg = i + 1; + break; + } + } + else + { + if (!PG_ARGISNULL(i) && i + 1 < nargs) + { + Datum result; + + /* recheck type */ + if (get_fn_expr_argtype(fcinfo->flinfo, i) != search_oid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("search expression has unexpected type"), + errhint("The decode expects \"%s\" type", + format_type_be(search_oid)))); + + result = FunctionCall2Coll(&fmgr_cache->eqop_finfo, + collation, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(i)); + + if (DatumGetBool(result)) + { + result_narg = i + 1; + break; + } + } + } + } + + if (result_narg >= 0 && !PG_ARGISNULL(result_narg)) + { + if (get_fn_expr_argtype(fcinfo->flinfo, result_narg) != result_oid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("result expression has unexpected type"), + errhint("The decode expects \"%s\" type", + format_type_be(result_oid)))); + + PG_RETURN_DATUM(PG_GETARG_DATUM(result_narg)); + } + else + 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; +} + +/* + * Support function for decode function + * + * It converts VARIADIC "any" to real types, and set real expected type. + */ +Datum +decode_support(PG_FUNCTION_ARGS) +{ + Node *rawreq = (Node *) PG_GETARG_POINTER(0); + Node *ret = NULL; + + if (IsA(rawreq, SupportRequestArgTypes)) + { + SupportRequestArgTypes *fd = (SupportRequestArgTypes *) rawreq; + FuncCandidateList candidate = fd->candidate; + + if (candidate->nargs >= 3 && !candidate->argnumbers && fd->nargs == candidate->nargs) + { + FuncCandidateList newc; + Oid search_oid; + Oid result_oid; + Oid search_typids[FUNC_MAX_ARGS]; + Oid result_typids[FUNC_MAX_ARGS]; + int search_nargs = 0; + int result_nargs = 0; + int i; + + newc = palloc(offsetof(struct _FuncCandidateList, args) + + candidate->nargs * sizeof(Oid)); + + /* + * Oracle should not to find most common types for numeric types, because + * type number is generic and widely used as integer type too. Using same + * setup is too simple for Postgres - using numeric instead int can has + * negative performance impact. + * + * When type is not TEXT, then the most common type is used. + */ + search_oid = fd->typeids[1] != UNKNOWNOID ? fd->typeids[1] : TEXTOID; + result_oid = fd->typeids[2] != UNKNOWNOID ? fd->typeids[2] : TEXTOID; + + if (search_oid != TEXTOID || result_oid != TEXTOID) + { + for (i = 0; i < fd->nargs; i++) + { + if (i == 0) + search_typids[search_nargs++] = fd->typeids[i]; + else if (i % 2) /* even position */ + { + if (i + 1 < fd->nargs) + search_typids[search_nargs++] = fd->typeids[i]; + else + result_typids[result_nargs++] = fd->typeids[i]; + } + else /* odd position */ + result_typids[result_nargs++] = fd->typeids[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"))); + } + } + + memcpy(newc, candidate, sizeof(struct _FuncCandidateList)); + + newc->rettype = result_oid; + + /* expression is casted to search */ + newc->args[0] = search_oid; + + for (i = 1; i < candidate->nargs; i += 2) + { + /* is pair or alone default? */ + if (i + 1 < candidate->nargs) + { + newc->args[i] = search_oid; + newc->args[i+1] = result_oid; + } + else + newc->args[i] = result_oid; + } + + candidate = newc; + } + else + candidate = NULL; + + fd->candidate = candidate; + + ret = (Node *) fd; + } + + PG_RETURN_POINTER(ret); +} diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index 67418346e6..9a52a8260b 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -30,6 +30,8 @@ typedef struct _FuncCandidateList struct _FuncCandidateList *next; int pathpos; /* for internal use of namespace lookup */ Oid oid; /* the function or operator's OID */ + Oid support_func; /* oid of support function if exists */ + Oid rettype; /* return type set by support function */ int nargs; /* number of arg types returned */ int nvargs; /* number of args to become variadic array */ int ndargs; /* number of defaulted args */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index eac909109c..02fb56ef24 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -3318,6 +3318,15 @@ proname => 'repeat', prorettype => 'text', proargtypes => 'text int4', prosrc => 'repeat' }, +{ oid => '6107', descr => 'choose a value from list', + proname => 'decode', provariadic => 'any', proisstrict => 'f', + prosupport => 'decode_support', prorettype => 'any', proargtypes => 'any', + prosrc => 'decode', + proallargtypes => '{any}', proargmodes => '{v}'}, +{ oid => '6108', descr => 'parser support for decode', + proname => 'decode_support', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'decode_support' }, + { oid => '1623', descr => 'convert SQL99 regexp pattern to POSIX style', proname => 'similar_escape', proisstrict => 'f', prorettype => 'text', proargtypes => 'text text', prosrc => 'similar_escape' }, diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index ffb4cd4bcc..daee08c25d 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_SupportRequestArgTypes /* in nodes/supportnodes.h */ } NodeTag; /* diff --git a/src/include/nodes/supportnodes.h b/src/include/nodes/supportnodes.h index 460d75bd2d..7a083e5241 100644 --- a/src/include/nodes/supportnodes.h +++ b/src/include/nodes/supportnodes.h @@ -38,7 +38,7 @@ struct PlannerInfo; /* avoid including pathnodes.h here */ struct IndexOptInfo; struct SpecialJoinInfo; - +struct _FuncCandidateList; /* * The Simplify request allows the support function to perform plan-time @@ -239,4 +239,26 @@ typedef struct SupportRequestIndexCondition * equivalent of the function call */ } SupportRequestIndexCondition; +/* + * PostgreSQL function's parameters are described by list of their types. + * For some specific functions this technique is not practical (require + * lot of overloaded functions), and better is using some algoritm. This + * node is used for functions that returns "any" type or has some "any" + * parameter. + * + * Support function is required for functions that returns "any" type. + * For "any" parameters it is optional, but it can increase a execution, + * because type detection (using exprType()) can be processed in parse + * time. + */ +typedef struct SupportRequestArgTypes +{ + NodeTag type; + + int nargs; + Oid *typeids; + Oid rettype; + struct _FuncCandidateList *candidate; +} SupportRequestArgTypes; + #endif /* SUPPORTNODES_H */