Oid mfinalfn = InvalidOid; /* can be omitted */
Oid sortop = InvalidOid; /* can be omitted */
Oid *aggArgTypes = parameterTypes->values;
- bool hasPolyArg;
- bool hasInternalArg;
bool mtransIsStrict = false;
Oid rettype;
Oid finaltype;
int nargs_finalfn;
Oid procOid;
TupleDesc tupDesc;
+ char *detailmsg;
int i;
ObjectAddress myself,
referenced;
FUNC_MAX_ARGS - 1,
FUNC_MAX_ARGS - 1)));
- /* check for polymorphic and INTERNAL arguments */
- hasPolyArg = false;
- hasInternalArg = false;
- for (i = 0; i < numArgs; i++)
- {
- if (IsPolymorphicType(aggArgTypes[i]))
- hasPolyArg = true;
- else if (aggArgTypes[i] == INTERNALOID)
- hasInternalArg = true;
- }
-
/*
* If transtype is polymorphic, must have polymorphic argument also; else
* we will have no way to deduce the actual transtype.
*/
- if (IsPolymorphicType(aggTransType) && !hasPolyArg)
+ detailmsg = check_valid_polymorphic_signature(aggTransType,
+ aggArgTypes,
+ numArgs);
+ if (detailmsg)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine transition data type"),
- errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
+ errdetail_internal("%s", detailmsg)));
/*
* Likewise for moving-aggregate transtype, if any
*/
- if (OidIsValid(aggmTransType) &&
- IsPolymorphicType(aggmTransType) && !hasPolyArg)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
- errmsg("cannot determine transition data type"),
- errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
+ if (OidIsValid(aggmTransType))
+ {
+ detailmsg = check_valid_polymorphic_signature(aggmTransType,
+ aggArgTypes,
+ numArgs);
+ if (detailmsg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot determine transition data type"),
+ errdetail_internal("%s", detailmsg)));
+ }
/*
* An ordered-set aggregate that is VARIADIC must be VARIADIC ANY. In
* that itself violates the rule against polymorphic result with no
* polymorphic input.)
*/
- if (IsPolymorphicType(finaltype) && !hasPolyArg)
+ detailmsg = check_valid_polymorphic_signature(finaltype,
+ aggArgTypes,
+ numArgs);
+ if (detailmsg)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot determine result data type"),
- errdetail("An aggregate returning a polymorphic type "
- "must have at least one polymorphic argument.")));
+ errdetail_internal("%s", detailmsg)));
/*
* Also, the return type can't be INTERNAL unless there's at least one
* for regular functions, but at the level of aggregates. We must test
* this explicitly because we allow INTERNAL as the transtype.
*/
- if (finaltype == INTERNALOID && !hasInternalArg)
+ detailmsg = check_valid_internal_signature(finaltype,
+ aggArgTypes,
+ numArgs);
+ if (detailmsg)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("unsafe use of pseudo-type \"internal\""),
- errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
+ errdetail_internal("%s", detailmsg)));
/*
* If a moving-aggregate implementation is supplied, look up its finalfn
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
#include "parser/parse_type.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
int allParamCount;
Oid *allParams;
char *paramModes = NULL;
- bool genericInParam = false;
- bool genericOutParam = false;
- bool anyrangeInParam = false;
- bool anyrangeOutParam = false;
- bool internalInParam = false;
- bool internalOutParam = false;
Oid variadicType = InvalidOid;
Acl *proacl = NULL;
Relation rel;
bool is_update;
ObjectAddress myself,
referenced;
+ char *detailmsg;
int i;
Oid trfid;
}
/*
- * Detect whether we have polymorphic or INTERNAL arguments. The first
- * loop checks input arguments, the second output arguments.
+ * Do not allow polymorphic return type unless there is a polymorphic
+ * input argument that we can use to deduce the actual return type.
*/
- for (i = 0; i < parameterCount; i++)
- {
- switch (parameterTypes->values[i])
- {
- case ANYARRAYOID:
- case ANYELEMENTOID:
- case ANYNONARRAYOID:
- case ANYENUMOID:
- genericInParam = true;
- break;
- case ANYRANGEOID:
- genericInParam = true;
- anyrangeInParam = true;
- break;
- case INTERNALOID:
- internalInParam = true;
- break;
- }
- }
+ detailmsg = check_valid_polymorphic_signature(returnType,
+ parameterTypes->values,
+ parameterCount);
+ if (detailmsg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot determine result data type"),
+ errdetail_internal("%s", detailmsg)));
+ /*
+ * Also, do not allow return type INTERNAL unless at least one input
+ * argument is INTERNAL.
+ */
+ detailmsg = check_valid_internal_signature(returnType,
+ parameterTypes->values,
+ parameterCount);
+ if (detailmsg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("unsafe use of pseudo-type \"internal\""),
+ errdetail_internal("%s", detailmsg)));
+
+ /*
+ * Apply the same tests to any OUT arguments.
+ */
if (allParameterTypes != PointerGetDatum(NULL))
{
for (i = 0; i < allParamCount; i++)
paramModes[i] == PROARGMODE_VARIADIC)
continue; /* ignore input-only params */
- switch (allParams[i])
- {
- case ANYARRAYOID:
- case ANYELEMENTOID:
- case ANYNONARRAYOID:
- case ANYENUMOID:
- genericOutParam = true;
- break;
- case ANYRANGEOID:
- genericOutParam = true;
- anyrangeOutParam = true;
- break;
- case INTERNALOID:
- internalOutParam = true;
- break;
- }
+ detailmsg = check_valid_polymorphic_signature(allParams[i],
+ parameterTypes->values,
+ parameterCount);
+ if (detailmsg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot determine result data type"),
+ errdetail_internal("%s", detailmsg)));
+ detailmsg = check_valid_internal_signature(allParams[i],
+ parameterTypes->values,
+ parameterCount);
+ if (detailmsg)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("unsafe use of pseudo-type \"internal\""),
+ errdetail_internal("%s", detailmsg)));
}
}
- /*
- * Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
- */
- if ((IsPolymorphicType(returnType) || genericOutParam)
- && !genericInParam)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
- errmsg("cannot determine result data type"),
- errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
-
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
- errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
-
- if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
- errmsg("unsafe use of pseudo-type \"internal\""),
- errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
-
+ /* Identify variadic argument type, if any */
if (paramModes != NULL)
{
/*
return rettype;
}
+/*
+ * check_valid_polymorphic_signature()
+ * Is a proposed function signature valid per polymorphism rules?
+ *
+ * Returns NULL if the signature is valid (either ret_type is not polymorphic,
+ * or it can be deduced from the given declared argument types). Otherwise,
+ * returns a palloc'd, already translated errdetail string saying why not.
+ */
+char *
+check_valid_polymorphic_signature(Oid ret_type,
+ const Oid *declared_arg_types,
+ int nargs)
+{
+ if (ret_type == ANYRANGEOID)
+ {
+ /*
+ * ANYRANGE requires an ANYRANGE input, else we can't tell which of
+ * several range types with the same element type to use.
+ */
+ for (int i = 0; i < nargs; i++)
+ {
+ if (declared_arg_types[i] == ret_type)
+ return NULL; /* OK */
+ }
+ return psprintf(_("A result of type %s requires at least one input of type %s."),
+ format_type_be(ret_type), format_type_be(ret_type));
+ }
+ else if (IsPolymorphicType(ret_type))
+ {
+ /* Otherwise, any polymorphic type can be deduced from any other */
+ for (int i = 0; i < nargs; i++)
+ {
+ if (IsPolymorphicType(declared_arg_types[i]))
+ return NULL; /* OK */
+ }
+ return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange."),
+ format_type_be(ret_type));
+ }
+ else
+ return NULL; /* OK, ret_type is not polymorphic */
+}
+
+/*
+ * check_valid_internal_signature()
+ * Is a proposed function signature valid per INTERNAL safety rules?
+ *
+ * Returns NULL if OK, or a suitable error message if ret_type is INTERNAL but
+ * none of the declared arg types are. (It's unsafe to create such a function
+ * since it would allow invocation of INTERNAL-consuming functions directly
+ * from SQL.) It's overkill to return the error detail message, since there
+ * is only one possibility, but we do it like this to keep the API similar to
+ * check_valid_polymorphic_signature().
+ */
+char *
+check_valid_internal_signature(Oid ret_type,
+ const Oid *declared_arg_types,
+ int nargs)
+{
+ if (ret_type == INTERNALOID)
+ {
+ for (int i = 0; i < nargs; i++)
+ {
+ if (declared_arg_types[i] == ret_type)
+ return NULL; /* OK */
+ }
+ return pstrdup(_("A result of type internal requires at least one input of type internal."));
+ }
+ else
+ return NULL; /* OK, ret_type is not INTERNAL */
+}
+
/* TypeCategory()
* Assign a category to the specified type OID.
Oid rettype,
bool allow_poly);
+extern char *check_valid_polymorphic_signature(Oid ret_type,
+ const Oid *declared_arg_types,
+ int nargs);
+extern char *check_valid_internal_signature(Oid ret_type,
+ const Oid *declared_arg_types,
+ int nargs);
+
extern CoercionPathType find_coercion_pathway(Oid targetTypeId,
Oid sourceTypeId,
CoercionContext ccontext,
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A result of type anyrange requires at least one input of type anyrange.
create function f1(x anyrange) returns anyarray as $$
begin
return array[lower(x), upper(x)];
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A result of type anyrange requires at least one input of type anyrange.
create function polyf(x anyrange) returns anyarray as $$
select array[lower(x), upper(x)]
$$ language sql;
CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- Case2 (R = P) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- Case4 (R = N) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot determine result data type
-DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
--
-- table functions
--
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A result of type anyrange requires at least one input of type anyrange.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+DETAIL: A result of type anyrange requires at least one input of type anyrange.
create function range_add_bounds(anyrange)
returns anyelement as 'select lower($1) + upper($1)' language sql;
select range_add_bounds(int4range(1, 17));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A result of type anyrange requires at least one input of type anyrange.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A result of type anyrange requires at least one input of type anyrange.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A result of type anyrange requires at least one input of type anyrange.