Re: proposal: new polymorphic types - commontype and commontypearray
| От | Tom Lane |
|---|---|
| Тема | Re: proposal: new polymorphic types - commontype and commontypearray |
| Дата | |
| Msg-id | 18363.1584290889@sss.pgh.pa.us обсуждение исходный текст |
| Ответ на | Re: proposal: new polymorphic types - commontype and commontypearray (Pavel Stehule <pavel.stehule@gmail.com>) |
| Ответы |
Re: proposal: new polymorphic types - commontype and commontypearray
|
| Список | pgsql-hackers |
Pavel Stehule <pavel.stehule@gmail.com> writes:
> Tom Lane <tgl@sss.pgh.pa.us> napsal:
>> Yeah, that's what I said. But does it really add anything beyond the
>> proposed text "A function returning a polymorphic type must have at least
>> one matching polymorphic argument"? I don't think it'd be terribly
>> helpful to say "A function returning anyelement must have at least one
>> anyelement, anyarray, anynonarray, anyenum, or anyrange argument", and
>> for sure such an error message would be a pain to maintain.
> The error message in your first patch is ok for all types without anyrange.
> A behave of this type is more strict and +/- different than from other
> polymorphic types.
Well, here's a version that does it like that, but personally I find these
messages too verbose and not an improvement on what I had before.
(This is also rebased over the stuff I committed yesterday.)
regards, tom lane
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 0b7face..7d887ea 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -93,8 +93,6 @@ AggregateCreate(const char *aggName,
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;
@@ -103,6 +101,7 @@ AggregateCreate(const char *aggName,
int nargs_finalfn;
Oid procOid;
TupleDesc tupDesc;
+ char *detailmsg;
int i;
ObjectAddress myself,
referenced;
@@ -131,36 +130,33 @@ AggregateCreate(const char *aggName,
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
@@ -492,12 +488,14 @@ AggregateCreate(const char *aggName,
* 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
@@ -505,11 +503,14 @@ AggregateCreate(const char *aggName,
* 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
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 423fd79..0cac936 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -32,6 +32,7 @@
#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"
@@ -97,12 +98,6 @@ ProcedureCreate(const char *procedureName,
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;
@@ -116,6 +111,7 @@ ProcedureCreate(const char *procedureName,
bool is_update;
ObjectAddress myself,
referenced;
+ char *detailmsg;
int i;
Oid trfid;
@@ -178,29 +174,34 @@ ProcedureCreate(const char *procedureName,
}
/*
- * 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++)
@@ -210,52 +211,26 @@ ProcedureCreate(const char *procedureName,
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)
{
/*
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 4a2b463..7a9b303 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1966,6 +1966,77 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
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.
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index ff9219d..8686eaa 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -80,6 +80,13 @@ extern Oid enforce_generic_type_consistency(const Oid *actual_arg_types,
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,
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 986417a..1573753 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -81,7 +81,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
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[],
@@ -93,11 +93,11 @@ CREATE AGGREGATE myaggp03b(*) (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
@@ -152,13 +152,13 @@ ERROR: function tfp(integer[], anyelement) does not exist
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,
@@ -174,21 +174,21 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
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,
@@ -218,11 +218,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
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[],
@@ -232,7 +232,7 @@ 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
@@ -286,21 +286,21 @@ ERROR: function tfp(integer[], anyelement) does not exist
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,
@@ -322,13 +322,13 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
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,
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index a70060b..cdfc43e 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1556,7 +1556,7 @@ DROP FUNCTION dup(anyelement);
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,
oranyrange.
--
-- table functions
--
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 348235a..a28741a 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -1371,12 +1371,12 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
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));
@@ -1521,14 +1521,14 @@ select * from table_succeed(int4range(1,11));
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.
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 410eaed..6407d3d 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4784,6 +4784,14 @@ SELECT * FROM pg_attribute
</indexterm>
<indexterm zone="datatype-pseudo">
+ <primary>anycompatible</primary>
+ </indexterm>
+
+ <indexterm zone="datatype-pseudo">
+ <primary>anycompatiblearray</primary>
+ </indexterm>
+
+ <indexterm zone="datatype-pseudo">
<primary>void</primary>
</indexterm>
@@ -4889,6 +4897,34 @@ SELECT * FROM pg_attribute
</row>
<row>
+ <entry><type>anycompatible</type></entry>
+ <entry>Indicates that a function accepts any data type. Values
+ are converted to real common type.
+ (see <xref linkend="extend-types-polymorphic"/>).</entry>
+ </row>
+
+ <row>
+ <entry><type>anycompatiblearray</type></entry>
+ <entry>Indicates that a function accepts any array data type. The
+ elements of array are converted to common type of these values.
+ (see <xref linkend="extend-types-polymorphic"/>).</entry>
+ </row>
+
+ <row>
+ <entry><type>anycompatiblenonarray</type></entry>
+ <entry>Indicates that a function accepts any non-array data type
+ (see <xref linkend="extend-types-polymorphic"/>).</entry>
+ </row>
+
+ <row>
+ <entry><type>anycompatiblerange</type></entry>
+ <entry>Indicates that a function accepts any range data type
+ (see <xref linkend="extend-types-polymorphic"/> and
+ <xref linkend="rangetypes"/>). The subtype can be used for
+ deduction of common type.</entry>
+ </row>
+
+ <row>
<entry><type>cstring</type></entry>
<entry>Indicates that a function accepts or returns a null-terminated C string.</entry>
</row>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 9ec1af7..05c0919 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -231,7 +231,7 @@
<para>
Five pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, <type>anycompatible</type> and <type>anycompatiblearray</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -268,6 +268,15 @@
</para>
<para>
+ Second family of polymorphic types are types <type>anycompatible</type> and
+ <type>anycompatiblearray</type>. These types are similar to types
+ <type>anyelement</type> and <type>anyarray</type>. The arguments declared
+ as <type>anyelement</type> requires same real type of passed values. For
+ <type>anycompatible</type>'s arguments is selected common type, and later
+ all these arguments are casted to this common type.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 76fd938..22540bb 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -404,10 +404,6 @@ ConstructTupleDescriptor(Relation heapRelation,
*/
keyType = amroutine->amkeytype;
- /*
- * Code below is concerned to the opclasses which are not used with
- * the included columns.
- */
if (i < indexInfo->ii_NumIndexKeyAttrs)
{
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(classObjectId[i]));
@@ -422,6 +418,10 @@ ConstructTupleDescriptor(Relation heapRelation,
* If keytype is specified as ANYELEMENT, and opcintype is
* ANYARRAY, then the attribute type must be an array (else it'd
* not have matched this opclass); use its element type.
+ *
+ * We could also allow ANYCOMPATIBLE/ANYCOMPATIBLEARRAY here, but
+ * there seems no need to do so; there's no reason to declare an
+ * opclass as taking ANYCOMPATIBLEARRAY rather than ANYARRAY.
*/
if (keyType == ANYELEMENTOID && opclassTup->opcintype == ANYARRAYOID)
{
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 0cac936..6cdda35 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -262,6 +262,9 @@ ProcedureCreate(const char *procedureName,
case ANYARRAYOID:
variadicType = ANYELEMENTOID;
break;
+ case ANYCOMPATIBLEARRAYOID:
+ variadicType = ANYCOMPATIBLEOID;
+ break;
default:
variadicType = get_element_type(allParams[i]);
if (!OidIsValid(variadicType))
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 5eac55a..694114a 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -320,6 +320,7 @@ interpret_function_parameter_list(ParseState *pstate,
switch (toid)
{
case ANYARRAYOID:
+ case ANYCOMPATIBLEARRAYOID:
case ANYOID:
/* okay */
break;
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 7a9b303..cf199cb 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -167,15 +167,17 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYOID ||
targetTypeId == ANYELEMENTOID ||
- targetTypeId == ANYNONARRAYOID)
+ targetTypeId == ANYNONARRAYOID ||
+ targetTypeId == ANYCOMPATIBLEOID ||
+ targetTypeId == ANYCOMPATIBLENONARRAYOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* Note: by returning the unmodified node here, we are saying that
* it's OK to treat an UNKNOWN constant as a valid input for a
- * function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be
- * all right, since an UNKNOWN value is still a perfectly valid Datum.
+ * function accepting one of these pseudotypes. This should be all
+ * right, since an UNKNOWN value is still a perfectly valid Datum.
*
* NB: we do NOT want a RelabelType here: the exposed type of the
* function argument must be its actual type, not the polymorphic
@@ -185,7 +187,9 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYCOMPATIBLEARRAYOID ||
+ targetTypeId == ANYCOMPATIBLERANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
@@ -193,10 +197,10 @@ coerce_type(ParseState *pstate, Node *node,
* These cases are unlike the ones above because the exposed type of
* the argument must be an actual array, enum, or range type. In
* particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * is, we just fall through; below, we'll call the pseudotype's input
+ * function, which will produce an error. Also, if what we have is a
+ * domain over array, enum, or range, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1387,6 +1391,99 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * 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, we can
+ * skip first i elements, because was checked in previous loop.
+ */
+ 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;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
@@ -1445,6 +1542,13 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
+ * 8) Previous rules are valid too for ANYCOMPATIBLE, ANYCOMPATIBLEARRAY,
+ * ANYCOMPATIBLENONARRAY and ANYCOMPATIBLERANGE with following exception.
+ * The used datatypes should not be strictly same. These types should be
+ * from same type category, and for any used type there should be implicit
+ * cast to selected one of these types.
+ * 9) There is not any relation between ANY types and ANYCOMPATIBLE types.
+ * Isn't possible derive ANY type from ANYCOMPATIBLE type and vice versa.
*
* Domains over arrays match ANYARRAY, and are immediately flattened to their
* base type. (Thus, for example, we will consider it a match if one ANYARRAY
@@ -1482,6 +1586,12 @@ check_generic_type_consistency(const Oid *actual_arg_types,
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
+ bool have_anycompatible_nonarray = false;
+ bool have_anycompatible_range = false;
+ bool have_generic_anycompatible = false;
+ Oid anycompatible_range_typeid = InvalidOid;
+ Oid anycompatible_actual_types[FUNC_MAX_ARGS];
+ int n_anycompatible_args = 0;
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1525,6 +1635,74 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYCOMPATIBLEOID ||
+ decl_type == ANYCOMPATIBLENONARRAYOID)
+ {
+ have_generic_anycompatible = true;
+ if (decl_type == ANYCOMPATIBLENONARRAYOID)
+ have_anycompatible_nonarray = true;
+
+ /* An unknown literal is no help for resolving actual types */
+ if (actual_type == UNKNOWNOID)
+ continue;
+
+ /* collect used type, reduce repeated values */
+ if (n_anycompatible_args == 0 ||
+ anycompatible_actual_types[n_anycompatible_args - 1] != actual_type)
+ anycompatible_actual_types[n_anycompatible_args++] = actual_type;
+ }
+ else if (decl_type == ANYCOMPATIBLEARRAYOID)
+ {
+ Oid anycompatible_elem_type;
+
+ have_generic_anycompatible = true;
+
+ if (actual_type == UNKNOWNOID)
+ continue;
+
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ anycompatible_elem_type = get_element_type(actual_type);
+
+ if (!OidIsValid(anycompatible_elem_type))
+ return false;
+
+ /* collect used type, reduce repeated values */
+ if (n_anycompatible_args == 0 ||
+ anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_elem_type)
+ anycompatible_actual_types[n_anycompatible_args++] = anycompatible_elem_type;
+ }
+ else if (decl_type == ANYCOMPATIBLERANGEOID)
+ {
+ Oid anycompatible_range_typelem;
+
+ have_generic_anycompatible = true;
+ have_anycompatible_range = true;
+
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+
+ /*
+ * range type is used just for derivation of common type, but
+ * range types should be same. Same behave like anyrange - cast
+ * between ranges are not supported.
+ */
+ if (OidIsValid(anycompatible_range_typeid) &&
+ anycompatible_range_typeid != actual_type)
+ return false;
+
+ anycompatible_range_typelem = get_range_subtype(actual_type);
+ if (!OidIsValid(anycompatible_range_typelem))
+ return false;
+
+ if (!OidIsValid(anycompatible_range_typeid))
+ anycompatible_range_typeid = actual_type;
+
+ /* collect used type, reduce repeated values */
+ if (n_anycompatible_args == 0 ||
+ anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_range_typelem)
+ anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1591,6 +1769,36 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
}
+ /* check anycompatible collected data */
+ if (have_generic_anycompatible)
+ {
+ /* we can check type consisteny when we have some not unknown types */
+ if (n_anycompatible_args > 0)
+ {
+ Oid anycompatible_typeid;
+
+ anycompatible_typeid = select_common_type_from_vector(n_anycompatible_args,
+ anycompatible_actual_types,
+ true);
+
+ if (!OidIsValid(anycompatible_typeid))
+ return false;
+
+ if (have_anycompatible_nonarray)
+ {
+ /*
+ * require the anycompatible type to not be an array or domain
+ * over array
+ */
+ if (type_is_array_domain(anycompatible_typeid))
+ return false;
+ }
+
+ if (have_anycompatible_range && !OidIsValid(anycompatible_range_typeid))
+ return false;
+ }
+ }
+
/* Looks valid */
return true;
}
@@ -1643,6 +1851,16 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
+ * 10) The relation between types from ANY family type are same like
+ * relations between types from ANYCOMPATIBLE family type, with one
+ * difference. The parameters with type from ANY family type requires
+ * exactly one actual type. The arguments with ANYCOMPATIBLE family type
+ * allows types that shares type category. Later polymorphic type is
+ * replaced by real type. This real type is one from actual types
+ * (polymorphic argument actual types) for that is available implicit
+ * cast from type of any related polymorphic arguments.
+ * 11) The arguments with ANY family type and ANYCOMPATIBLE family type
+ * are independent.
*
* Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments,
* respectively, and are immediately flattened to their base type. (In
@@ -1673,11 +1891,15 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
bool allow_poly)
{
int j;
- bool have_generics = false;
+ bool have_generics_any = false;
+ bool have_generics_anycompatible = false;
bool have_unknowns = false;
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid anycompatible_typeid = InvalidOid;
+ Oid anycompatible_array_typeid = InvalidOid;
+ Oid anycompatible_range_typeid = InvalidOid;
Oid array_typelem;
Oid range_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
@@ -1685,6 +1907,11 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
+ bool have_anycompatible_array = (rettype == ANYCOMPATIBLEARRAYOID);
+ bool have_anycompatible_range = (rettype == ANYCOMPATIBLERANGEOID);
+ Oid anycompatible_actual_types[FUNC_MAX_ARGS];
+ int n_anycompatible_args = 0;
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1699,7 +1926,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
decl_type == ANYNONARRAYOID ||
decl_type == ANYENUMOID)
{
- have_generics = have_anyelement = true;
+ have_generics_any = have_anyelement = true;
if (decl_type == ANYNONARRAYOID)
have_anynonarray = true;
else if (decl_type == ANYENUMOID)
@@ -1722,14 +1949,18 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYARRAYOID)
{
- have_generics = true;
+ have_generics_any = true;
+ have_anycompatible_array = true;
+
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
continue;
}
+
if (allow_poly && decl_type == actual_type)
continue; /* no new information here */
+
actual_type = getBaseType(actual_type); /* flatten domains */
if (OidIsValid(array_typeid) && actual_type != array_typeid)
ereport(ERROR,
@@ -1742,7 +1973,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics_any = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1760,128 +1991,300 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
- }
+ else if (decl_type == ANYCOMPATIBLEOID ||
+ decl_type == ANYCOMPATIBLENONARRAYOID)
+ {
+ have_generics_anycompatible = true;
- /*
- * Fast Track: if none of the arguments are polymorphic, return the
- * unmodified rettype. We assume it can't be polymorphic either.
- */
- if (!have_generics)
- return rettype;
+ if (decl_type == ANYCOMPATIBLENONARRAYOID)
+ have_anycompatible_nonarray = true;
- /* Get the element type based on the array type, if we have one */
- if (OidIsValid(array_typeid))
- {
- if (array_typeid == ANYARRAYOID && !have_anyelement)
- {
- /* Special case for ANYARRAY input: okay iff no ANYELEMENT */
- array_typelem = ANYELEMENTOID;
+ /*
+ * because declared type will be replaced every time, we don't
+ * need some special work for unknown types.
+ */
+ if (actual_type == UNKNOWNOID)
+ continue;
+
+ if (allow_poly && decl_type == actual_type)
+ continue;
+
+ /* collect used type, reduce repeated values */
+ if (n_anycompatible_args == 0 ||
+ anycompatible_actual_types[n_anycompatible_args - 1] != actual_type)
+ anycompatible_actual_types[n_anycompatible_args++] = actual_type;
}
- else
+ else if (decl_type == ANYCOMPATIBLEARRAYOID)
{
- array_typelem = get_element_type(array_typeid);
- if (!OidIsValid(array_typelem))
+ Oid anycompatible_elem_type;
+
+ have_generics_anycompatible = true;
+ have_anycompatible_array = true;
+
+ if (actual_type == UNKNOWNOID)
+ continue;
+
+ if (allow_poly && decl_type == actual_type)
+ continue;
+
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ anycompatible_elem_type = get_element_type(actual_type);
+
+ if (!OidIsValid(anycompatible_elem_type))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not an array but type %s",
- "anyarray", format_type_be(array_typeid))));
- }
+ "anyarray", format_type_be(actual_type))));
- if (!OidIsValid(elem_typeid))
- {
- /*
- * if we don't have an element type yet, use the one we just got
- */
- elem_typeid = array_typelem;
+ /* collect used type, reduce repeated values */
+ if (n_anycompatible_args == 0 ||
+ anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_elem_type)
+ anycompatible_actual_types[n_anycompatible_args++] = anycompatible_elem_type;
}
- else if (array_typelem != elem_typeid)
+ else if (decl_type == ANYCOMPATIBLERANGEOID)
{
- /* otherwise, they better match */
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("argument declared %s is not consistent with argument declared %s",
- "anyarray", "anyelement"),
- errdetail("%s versus %s",
- format_type_be(array_typeid),
- format_type_be(elem_typeid))));
+ Oid anycompatible_range_typelem;
+
+ have_generics_anycompatible = true;
+ have_anycompatible_range = true;
+
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+
+ if (OidIsValid(anycompatible_range_typeid) &&
+ actual_type != anycompatible_range_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anycompatiblerange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(anycompatible_range_typeid),
+ format_type_be(actual_type))));
+
+ anycompatible_range_typeid = actual_type;
+ anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid);
+
+ if (!OidIsValid(anycompatible_range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anycompatiblerange",
+ format_type_be(anycompatible_range_typeid))));
+
+ /* collect used type, reduce repeated values */
+ if (n_anycompatible_args == 0 ||
+ anycompatible_actual_types[n_anycompatible_args - 1] != anycompatible_range_typelem)
+ anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
}
- /* Get the element type based on the range type, if we have one */
- if (OidIsValid(range_typeid))
+ /*
+ * Fast Track: if none of the arguments are polymorphic, return the
+ * unmodified rettype. We assume it can't be polymorphic either.
+ */
+ if (!have_generics_any && !have_generics_anycompatible)
+ return rettype;
+
+ if (have_generics_any)
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ /* Get the element type based on the array type, if we have one */
+ if (OidIsValid(array_typeid))
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
- range_typelem = ANYELEMENTOID;
+ if (array_typeid == ANYARRAYOID && !have_anyelement)
+ {
+ /* Special case for ANYARRAY input: okay iff no ANYELEMENT */
+ array_typelem = ANYELEMENTOID;
+ }
+ else
+ {
+ array_typelem = get_element_type(array_typeid);
+ if (!OidIsValid(array_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not an array but type %s",
+ "anyarray", format_type_be(array_typeid))));
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just
+ * got
+ */
+ elem_typeid = array_typelem;
+ }
+ else if (array_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anyarray", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(array_typeid),
+ format_type_be(elem_typeid))));
+ }
}
- else
+
+ /* Get the element type based on the range type, if we have one */
+ if (OidIsValid(range_typeid))
{
- range_typelem = get_range_subtype(range_typeid);
- if (!OidIsValid(range_typelem))
+ if (range_typeid == ANYRANGEOID && !have_anyelement)
+ {
+ /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ range_typelem = ANYELEMENTOID;
+ }
+ else
+ {
+ range_typelem = get_range_subtype(range_typeid);
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange",
+ format_type_be(range_typeid))));
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just
+ * got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("argument declared %s is not a range type but type %s",
- "anyrange",
- format_type_be(range_typeid))));
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anyrange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(range_typeid),
+ format_type_be(elem_typeid))));
+ }
}
if (!OidIsValid(elem_typeid))
{
+ if (allow_poly)
+ {
+ elem_typeid = ANYELEMENTOID;
+ array_typeid = ANYARRAYOID;
+ range_typeid = ANYRANGEOID;
+ }
+ else
+ {
+ /* Only way to get here is if all the generic args are UNKNOWN */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not determine polymorphic type because input has type %s",
+ "unknown")));
+ }
+ }
+
+ if (have_anynonarray && elem_typeid != ANYELEMENTOID)
+ {
/*
- * if we don't have an element type yet, use the one we just got
+ * require the element type to not be an array or domain over
+ * array
*/
- elem_typeid = range_typelem;
+ if (type_is_array_domain(elem_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type matched to anynonarray is an array type: %s",
+ format_type_be(elem_typeid))));
}
- else if (range_typelem != elem_typeid)
+
+ if (have_anyenum && elem_typeid != ANYELEMENTOID)
{
- /* otherwise, they better match */
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("argument declared %s is not consistent with argument declared %s",
- "anyrange", "anyelement"),
- errdetail("%s versus %s",
- format_type_be(range_typeid),
- format_type_be(elem_typeid))));
+ /* require the element type to be an enum */
+ if (!type_is_enum(elem_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type matched to anyenum is not an enum type: %s",
+ format_type_be(elem_typeid))));
}
}
- if (!OidIsValid(elem_typeid))
+ if (have_generics_anycompatible)
{
- if (allow_poly)
+ if (n_anycompatible_args > 0)
{
- elem_typeid = ANYELEMENTOID;
- array_typeid = ANYARRAYOID;
- range_typeid = ANYRANGEOID;
+ anycompatible_typeid = select_common_type_from_vector(n_anycompatible_args,
+ anycompatible_actual_types,
+ false);
+
+ if (have_anycompatible_array)
+ {
+ anycompatible_array_typeid = get_array_type(anycompatible_typeid);
+
+ if (!OidIsValid(anycompatible_array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(anycompatible_typeid))));
+ }
+
+ /* anycompatible_range_typid should be defined already */
+ /* XXX this error message is not very on-point */
+ if (have_anycompatible_range &&
+ !OidIsValid(anycompatible_range_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ "anycompatiblerange")));
+
+ if (have_anycompatible_nonarray)
+ {
+ /*
+ * require the element type to not be an array or domain over
+ * array
+ */
+ if (type_is_array_domain(anycompatible_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type matched to anynonarray is an array type: %s",
+ format_type_be(anycompatible_typeid))));
+ }
}
else
{
- /* Only way to get here is if all the generic args are UNKNOWN */
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("could not determine polymorphic type because input has type %s",
- "unknown")));
+ if (allow_poly)
+ {
+ anycompatible_typeid = ANYCOMPATIBLEOID;
+ anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
+ anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
+ }
+ else
+ {
+ /* Only way to get here is if all the generic args are UNKNOWN */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not determine polymorphic common type because input has type %s",
+ "unknown")));
+ }
}
- }
- if (have_anynonarray && elem_typeid != ANYELEMENTOID)
- {
- /* require the element type to not be an array or domain over array */
- if (type_is_array_domain(elem_typeid))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("type matched to anynonarray is an array type: %s",
- format_type_be(elem_typeid))));
- }
+ /* replace polymorphic common types by selected common types */
+ for (j = 0; j < nargs; j++)
+ {
+ Oid decl_type = declared_arg_types[j];
- if (have_anyenum && elem_typeid != ANYELEMENTOID)
- {
- /* require the element type to be an enum */
- if (!type_is_enum(elem_typeid))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("type matched to anyenum is not an enum type: %s",
- format_type_be(elem_typeid))));
+ if (decl_type == ANYCOMPATIBLEOID ||
+ decl_type == ANYCOMPATIBLENONARRAYOID) /* XXX seems wrong? */
+ declared_arg_types[j] = anycompatible_typeid;
+ else if (decl_type == ANYCOMPATIBLEARRAYOID)
+ declared_arg_types[j] = anycompatible_array_typeid;
+ else if (decl_type == ANYCOMPATIBLERANGEOID)
+ declared_arg_types[j] = anycompatible_range_typeid;
+ }
}
/*
@@ -1962,6 +2365,35 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
rettype == ANYENUMOID)
return elem_typeid;
+ if (rettype == ANYCOMPATIBLEOID)
+ {
+ if (!OidIsValid(anycompatible_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type")));
+ return anycompatible_typeid;
+ }
+
+ if (rettype == ANYCOMPATIBLEARRAYOID)
+ {
+ if (!OidIsValid(anycompatible_array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common array type")));
+ return anycompatible_array_typeid;
+ }
+
+ /* if we return ANYRANGE use the appropriate argument type */
+ if (rettype == ANYCOMPATIBLERANGEOID)
+ {
+ if (!OidIsValid(anycompatible_range_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ "anycompatiblerange")));
+ return anycompatible_range_typeid;
+ }
+
/* we don't return a generic type; send back the original return type */
return rettype;
}
@@ -1979,11 +2411,12 @@ check_valid_polymorphic_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
- if (ret_type == ANYRANGEOID)
+ if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
{
/*
* ANYRANGE requires an ANYRANGE input, else we can't tell which of
- * several range types with the same element type to use.
+ * several range types with the same element type to use. Likewise
+ * for ANYCOMPATIBLERANGE.
*/
for (int i = 0; i < nargs; i++)
{
@@ -1993,17 +2426,28 @@ check_valid_polymorphic_signature(Oid ret_type,
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))
+ else if (IsPolymorphicTypeFamily1(ret_type))
{
- /* Otherwise, any polymorphic type can be deduced from any other */
+ /* Otherwise, any family-1 type can be deduced from any other */
for (int i = 0; i < nargs; i++)
{
- if (IsPolymorphicType(declared_arg_types[i]))
+ if (IsPolymorphicTypeFamily1(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 if (IsPolymorphicTypeFamily2(ret_type))
+ {
+ /* Otherwise, any family-2 type can be deduced from any other */
+ for (int i = 0; i < nargs; i++)
+ {
+ if (IsPolymorphicTypeFamily2(declared_arg_types[i]))
+ return NULL; /* OK */
+ }
+ return psprintf(_("A result of type %s requires at least one input of type anycompatible, anycompatiblearray,
anycompatiblenonarray,or anycompatiblerange."),
+ format_type_be(ret_type));
+ }
else
return NULL; /* OK, ret_type is not polymorphic */
}
@@ -2108,8 +2552,9 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (srctype == targettype)
return true;
- /* Anything is coercible to ANY or ANYELEMENT */
- if (targettype == ANYOID || targettype == ANYELEMENTOID)
+ /* Anything is coercible to ANY or ANYELEMENT or ANYCOMPATIBLE */
+ if (targettype == ANYOID || targettype == ANYELEMENTOID ||
+ targettype == ANYCOMPATIBLEOID)
return true;
/* If srctype is a domain, reduce to its base type */
@@ -2120,13 +2565,13 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (srctype == targettype)
return true;
- /* Also accept any array type as coercible to ANYARRAY */
- if (targettype == ANYARRAYOID)
+ /* Also accept any array type as coercible to ANY[COMPATIBLE]ARRAY */
+ if (targettype == ANYARRAYOID || targettype == ANYCOMPATIBLEARRAYOID)
if (type_is_array(srctype))
return true;
- /* Also accept any non-array type as coercible to ANYNONARRAY */
- if (targettype == ANYNONARRAYOID)
+ /* Also accept any non-array type as coercible to ANY[COMPATIBLE]NONARRAY */
+ if (targettype == ANYNONARRAYOID || targettype == ANYCOMPATIBLENONARRAYOID)
if (!type_is_array(srctype))
return true;
@@ -2135,8 +2580,8 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_enum(srctype))
return true;
- /* Also accept any range type as coercible to ANYRANGE */
- if (targettype == ANYRANGEOID)
+ /* Also accept any range type as coercible to ANY[COMPATIBLE]RANGE */
+ if (targettype == ANYRANGEOID || targettype == ANYCOMPATIBLERANGEOID)
if (type_is_range(srctype))
return true;
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index f78420e..8bb00ab 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -195,7 +195,7 @@ json_categorize_type(Oid typoid,
default:
/* Check for arrays and composites */
if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
- || typoid == RECORDARRAYOID)
+ || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
*tcategory = JSONTYPE_ARRAY;
else if (type_is_rowtype(typoid)) /* includes RECORDOID */
*tcategory = JSONTYPE_COMPOSITE;
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index b961d29..1e9ca04 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -677,7 +677,7 @@ jsonb_categorize_type(Oid typoid,
default:
/* Check for arrays and composites */
if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
- || typoid == RECORDARRAYOID)
+ || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
*tcategory = JSONBTYPE_ARRAY;
else if (type_is_rowtype(typoid)) /* includes RECORDOID */
*tcategory = JSONBTYPE_COMPOSITE;
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 9eee03c..3d6b2f9 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -169,6 +169,26 @@ anyarray_send(PG_FUNCTION_ARGS)
}
/*
+ * anycompatiblearray
+ *
+ * We may as well allow output, since we do for anyarray.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblearray);
+PSEUDOTYPE_DUMMY_RECEIVE_FUNC(anycompatiblearray);
+
+Datum
+anycompatiblearray_out(PG_FUNCTION_ARGS)
+{
+ return array_out(fcinfo);
+}
+
+Datum
+anycompatiblearray_send(PG_FUNCTION_ARGS)
+{
+ return array_send(fcinfo);
+}
+
+/*
* anyenum
*
* We may as well allow output, since enum_out will in fact work.
@@ -195,6 +215,19 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anycompatiblerange
+ *
+ * We may as well allow output, since range_out will in fact work.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblerange);
+
+Datum
+anycompatiblerange_out(PG_FUNCTION_ARGS)
+{
+ return range_out(fcinfo);
+}
+
+/*
* void
*
* We support void_in so that PL functions can return VOID without any
@@ -316,3 +349,5 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler);
PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatible);
+PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatiblenonarray);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 4e9d21b..78ed857 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -552,7 +552,9 @@ resolve_anyrange_from_others(polymorphic_actuals *actuals)
* with concrete data types deduced from the input arguments.
* declared_args is an oidvector of the function's declared input arg types
* (showing which are polymorphic), and call_expr is the call expression.
- * Returns true if able to deduce all types, false if not.
+ *
+ * Returns true if able to deduce all types, false if necessary information
+ * is not provided (call_expr is NULL or arg types aren't identifiable).
*/
static bool
resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
@@ -564,8 +566,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anycompatible_result = false;
+ bool have_anycompatible_array_result = false;
+ bool have_anycompatible_range_result = false;
polymorphic_actuals poly_actuals;
+ polymorphic_actuals anyc_actuals;
Oid anycollation = InvalidOid;
+ Oid anycompatcollation = InvalidOid;
int i;
/* See if there are any polymorphic outputs; quick out if not */
@@ -587,6 +594,19 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anyrange_result = true;
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ have_polymorphic_result = true;
+ have_anycompatible_result = true;
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ have_polymorphic_result = true;
+ have_anycompatible_array_result = true;
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ have_polymorphic_result = true;
+ have_anycompatible_range_result = true;
+ break;
default:
break;
}
@@ -596,12 +616,16 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/*
* Otherwise, extract actual datatype(s) from input arguments. (We assume
- * the parser already validated consistency of the arguments.)
+ * the parser already validated consistency of the arguments. Also, for
+ * the ANYCOMPATIBLE pseudotype family, we expect that all matching
+ * arguments were coerced to the selected common supertype, so that it
+ * doesn't matter which one's exposed type we look at.)
*/
if (!call_expr)
return false; /* no hope */
memset(&poly_actuals, 0, sizeof(poly_actuals));
+ memset(&anyc_actuals, 0, sizeof(anyc_actuals));
for (i = 0; i < nargs; i++)
{
@@ -636,6 +660,34 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ if (!OidIsValid(anyc_actuals.anyelement_type))
+ {
+ anyc_actuals.anyelement_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(anyc_actuals.anyelement_type))
+ return false;
+ }
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ if (!OidIsValid(anyc_actuals.anyarray_type))
+ {
+ anyc_actuals.anyarray_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(anyc_actuals.anyarray_type))
+ return false;
+ }
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ if (!OidIsValid(anyc_actuals.anyrange_type))
+ {
+ anyc_actuals.anyrange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(anyc_actuals.anyrange_type))
+ return false;
+ }
+ break;
default:
break;
}
@@ -651,18 +703,33 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
+ resolve_anyelement_from_others(&anyc_actuals);
+
+ if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
+ resolve_anyarray_from_others(&anyc_actuals);
+
+ if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
+ resolve_anyrange_from_others(&anyc_actuals);
+
/*
* Identify the collation to use for polymorphic OUT parameters. (It'll
- * necessarily be the same for both anyelement and anyarray.) Note that
- * range types are not collatable, so any possible internal collation of a
- * range type is not considered here.
+ * necessarily be the same for both anyelement and anyarray, likewise for
+ * anycompatible and anycompatiblearray.) Note that range types are not
+ * collatable, so any possible internal collation of a range type is not
+ * considered here.
*/
if (OidIsValid(poly_actuals.anyelement_type))
anycollation = get_typcollation(poly_actuals.anyelement_type);
else if (OidIsValid(poly_actuals.anyarray_type))
anycollation = get_typcollation(poly_actuals.anyarray_type);
- if (OidIsValid(anycollation))
+ if (OidIsValid(anyc_actuals.anyelement_type))
+ anycompatcollation = get_typcollation(anyc_actuals.anyelement_type);
+ else if (OidIsValid(anyc_actuals.anyarray_type))
+ anycompatcollation = get_typcollation(anyc_actuals.anyarray_type);
+
+ if (OidIsValid(anycollation) || OidIsValid(anycompatcollation))
{
/*
* The types are collatable, so consider whether to use a nondefault
@@ -672,7 +739,12 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
Oid inputcollation = exprInputCollation(call_expr);
if (OidIsValid(inputcollation))
- anycollation = inputcollation;
+ {
+ if (OidIsValid(anycollation))
+ anycollation = inputcollation;
+ if (OidIsValid(anycompatcollation))
+ anycompatcollation = inputcollation;
+ }
}
/* And finally replace the tuple column types as needed */
@@ -708,6 +780,31 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anyc_actuals.anyelement_type,
+ -1,
+ 0);
+ TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anyc_actuals.anyarray_type,
+ -1,
+ 0);
+ TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anyc_actuals.anyrange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a range type */
+ break;
default:
break;
}
@@ -720,7 +817,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
* Given the declared argument types and modes for a function, replace any
* polymorphic types (ANYELEMENT etc) in argtypes[] with concrete data types
* deduced from the input arguments found in call_expr.
- * Returns true if able to deduce all types, false if not.
+ *
+ * Returns true if able to deduce all types, false if necessary information
+ * is not provided (call_expr is NULL or arg types aren't identifiable).
*
* This is the same logic as resolve_polymorphic_tupdesc, but with a different
* argument representation, and slightly different output responsibilities.
@@ -735,16 +834,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anycompatible_result = false;
+ bool have_anycompatible_array_result = false;
+ bool have_anycompatible_range_result = false;
polymorphic_actuals poly_actuals;
+ polymorphic_actuals anyc_actuals;
int inargno;
int i;
/*
* First pass: resolve polymorphic inputs, check for outputs. As in
* resolve_polymorphic_tupdesc, we rely on the parser to have enforced
- * type consistency.
+ * type consistency and coerced ANYCOMPATIBLE args to a common supertype.
*/
memset(&poly_actuals, 0, sizeof(poly_actuals));
+ memset(&anyc_actuals, 0, sizeof(anyc_actuals));
inargno = 0;
for (i = 0; i < numargs; i++)
{
@@ -808,6 +912,61 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = poly_actuals.anyrange_type;
}
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anycompatible_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(anyc_actuals.anyelement_type))
+ {
+ anyc_actuals.anyelement_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(anyc_actuals.anyelement_type))
+ return false;
+ }
+ argtypes[i] = anyc_actuals.anyelement_type;
+ }
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anycompatible_array_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(anyc_actuals.anyarray_type))
+ {
+ anyc_actuals.anyarray_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(anyc_actuals.anyarray_type))
+ return false;
+ }
+ argtypes[i] = anyc_actuals.anyarray_type;
+ }
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anycompatible_range_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(anyc_actuals.anyrange_type))
+ {
+ anyc_actuals.anyrange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(anyc_actuals.anyrange_type))
+ return false;
+ }
+ argtypes[i] = anyc_actuals.anyrange_type;
+ }
+ break;
default:
break;
}
@@ -829,6 +988,15 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
+ resolve_anyelement_from_others(&anyc_actuals);
+
+ if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
+ resolve_anyarray_from_others(&anyc_actuals);
+
+ if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
+ resolve_anyrange_from_others(&anyc_actuals);
+
/* And finally replace the output column types as needed */
for (i = 0; i < numargs; i++)
{
@@ -845,6 +1013,16 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = poly_actuals.anyrange_type;
break;
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
+ argtypes[i] = anyc_actuals.anyelement_type;
+ break;
+ case ANYCOMPATIBLEARRAYOID:
+ argtypes[i] = anyc_actuals.anyarray_type;
+ break;
+ case ANYCOMPATIBLERANGEOID:
+ argtypes[i] = anyc_actuals.anyrange_type;
+ break;
default:
break;
}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7fb574f..8387238 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7106,6 +7106,42 @@
{ oid => '268', descr => 'I/O',
proname => 'table_am_handler_out', prorettype => 'cstring',
proargtypes => 'table_am_handler', prosrc => 'table_am_handler_out' },
+{ oid => '9559', descr => 'I/O',
+ proname => 'anycompatible_in', prorettype => 'anycompatible',
+ proargtypes => 'cstring', prosrc => 'anycompatible_in' },
+{ oid => '9560', descr => 'I/O',
+ proname => 'anycompatible_out', prorettype => 'cstring',
+ proargtypes => 'anycompatible', prosrc => 'anycompatible_out' },
+{ oid => '9561', descr => 'I/O',
+ proname => 'anycompatiblearray_in', prorettype => 'anycompatiblearray',
+ proargtypes => 'cstring', prosrc => 'anycompatiblearray_in' },
+{ oid => '9562', descr => 'I/O',
+ proname => 'anycompatiblearray_out', provolatile => 's',
+ prorettype => 'cstring', proargtypes => 'anycompatiblearray',
+ prosrc => 'anycompatiblearray_out' },
+{ oid => '9563', descr => 'I/O',
+ proname => 'anycompatiblearray_recv', provolatile => 's',
+ prorettype => 'anycompatiblearray', proargtypes => 'internal',
+ prosrc => 'anycompatiblearray_recv' },
+{ oid => '9564', descr => 'I/O',
+ proname => 'anycompatiblearray_send', provolatile => 's',
+ prorettype => 'bytea', proargtypes => 'anycompatiblearray',
+ prosrc => 'anycompatiblearray_send' },
+{ oid => '9565', descr => 'I/O',
+ proname => 'anycompatiblenonarray_in', prorettype => 'anycompatiblenonarray',
+ proargtypes => 'cstring', prosrc => 'anycompatiblenonarray_in' },
+{ oid => '9566', descr => 'I/O',
+ proname => 'anycompatiblenonarray_out', prorettype => 'cstring',
+ proargtypes => 'anycompatiblenonarray',
+ prosrc => 'anycompatiblenonarray_out' },
+{ oid => '9567', descr => 'I/O',
+ proname => 'anycompatiblerange_in', provolatile => 's',
+ prorettype => 'anycompatiblerange', proargtypes => 'cstring oid int4',
+ prosrc => 'anycompatiblerange_in' },
+{ oid => '9568', descr => 'I/O',
+ proname => 'anycompatiblerange_out', provolatile => 's',
+ prorettype => 'cstring', proargtypes => 'anycompatiblerange',
+ prosrc => 'anycompatiblerange_out' },
# tablesample method handlers
{ oid => '3313', descr => 'BERNOULLI tablesample method handler',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index b00597d..20d5167 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -590,5 +590,30 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '9550',
+ descr => 'pseudo-type representing a polymorphic common type',
+ typname => 'anycompatible', typlen => '4', typbyval => 't', typtype => 'p',
+ typcategory => 'P', typinput => 'anycompatible_in',
+ typoutput => 'anycompatible_out', typreceive => '-', typsend => '-',
+ typalign => 'i' },
+{ oid => '9551',
+ descr => 'pseudo-type representing an array of polymorphic common type elements',
+ typname => 'anycompatiblearray', typlen => '-1', typbyval => 'f',
+ typtype => 'p', typcategory => 'P', typinput => 'anycompatiblearray_in',
+ typoutput => 'anycompatiblearray_out',
+ typreceive => 'anycompatiblearray_recv', typsend => 'anycompatiblearray_send',
+ typalign => 'd', typstorage => 'x' },
+{ oid => '9552',
+ descr => 'pseudo-type representing a polymorphic common type that is not an array',
+ typname => 'anycompatiblenonarray', typlen => '4', typbyval => 't',
+ typtype => 'p', typcategory => 'P', typinput => 'anycompatiblenonarray_in',
+ typoutput => 'anycompatiblenonarray_out', typreceive => '-', typsend => '-',
+ typalign => 'i' },
+{ oid => '9553',
+ descr => 'pseudo-type representing a polymorphic common type that is a range',
+ typname => 'anycompatiblerange', typlen => '-1', typbyval => 'f',
+ typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
+ typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
+ typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 9789094..7b37562 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -295,12 +295,23 @@ typedef FormData_pg_type *Form_pg_type;
/* Is a type OID a polymorphic pseudotype? (Beware of multiple evaluation) */
#define IsPolymorphicType(typid) \
+ (IsPolymorphicTypeFamily1(typid) || \
+ IsPolymorphicTypeFamily2(typid))
+
+/* Code not part of polymorphic type resolution should not use these macros: */
+#define IsPolymorphicTypeFamily1(typid) \
((typid) == ANYELEMENTOID || \
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
(typid) == ANYRANGEOID)
+#define IsPolymorphicTypeFamily2(typid) \
+ ((typid) == ANYCOMPATIBLEOID || \
+ (typid) == ANYCOMPATIBLEARRAYOID || \
+ (typid) == ANYCOMPATIBLENONARRAYOID || \
+ (typid) == ANYCOMPATIBLERANGEOID)
+
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index c8e43e6..828ff5a 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -507,11 +507,13 @@ do_compile(FunctionCallInfo fcinfo,
{
if (forValidator)
{
- if (rettypeid == ANYARRAYOID)
+ if (rettypeid == ANYARRAYOID ||
+ rettypeid == ANYCOMPATIBLEARRAYOID)
rettypeid = INT4ARRAYOID;
- else if (rettypeid == ANYRANGEOID)
+ else if (rettypeid == ANYRANGEOID ||
+ rettypeid == ANYCOMPATIBLERANGEOID)
rettypeid = INT4RANGEOID;
- else /* ANYELEMENT or ANYNONARRAY */
+ else /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
}
@@ -2493,12 +2495,16 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID: /* XXX dubious */
+ case ANYCOMPATIBLEOID:
+ case ANYCOMPATIBLENONARRAYOID:
argtypes[i] = INT4OID;
break;
case ANYARRAYOID:
+ case ANYCOMPATIBLEARRAYOID:
argtypes[i] = INT4ARRAYOID;
break;
case ANYRANGEOID:
+ case ANYCOMPATIBLERANGEOID:
argtypes[i] = INT4RANGEOID;
break;
default:
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 1573753..8057c20 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -1547,3 +1547,177 @@ View definition:
drop view dfview;
drop function dfunc(anyelement, anyelement, bool);
+create or replace function cttestfunc01(anycompatible, anycompatible)
+returns anycompatible as $$
+begin
+ if $1 > $2 then
+ return $1;
+ else
+ return $2;
+ end if;
+end;
+$$ language plpgsql;
+create or replace function cttestfunc02(anycompatible, anycompatible)
+returns anycompatiblearray as $$
+begin
+ return ARRAY[$1, $2];
+end;
+$$ language plpgsql;
+create or replace function cttestfunc03(anycompatiblearray)
+returns anycompatible as $$
+begin
+ return $1[1];
+end;
+$$ language plpgsql;
+create or replace function cttestfunc04(variadic anycompatiblearray)
+returns anycompatible as $$
+begin
+ return (select min(v) from unnest($1) g(v));
+end;
+$$ language plpgsql;
+create or replace function cttestfunc05(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+begin
+ return $1;
+end;
+$$ language plpgsql;
+create or replace function cttestfunc06(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+begin
+ return ARRAY[$1, $2];
+end;
+$$ language plpgsql;
+create or replace function cttestfunc07(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+ select $1
+$$ language sql;
+create or replace function cttestfunc08(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+select array[$1, $2]
+$$ language sql;
+select cttestfunc01(10, 20);
+ cttestfunc01
+--------------
+ 20
+(1 row)
+
+select cttestfunc01(10.1, 20.1);
+ cttestfunc01
+--------------
+ 20.1
+(1 row)
+
+select cttestfunc01(10, 20.1);
+ cttestfunc01
+--------------
+ 20.1
+(1 row)
+
+select cttestfunc02(10, 20);
+ cttestfunc02
+--------------
+ {10,20}
+(1 row)
+
+select cttestfunc02(10.1, 20.1);
+ cttestfunc02
+--------------
+ {10.1,20.1}
+(1 row)
+
+select cttestfunc02(10, 20.1);
+ cttestfunc02
+--------------
+ {10,20.1}
+(1 row)
+
+select cttestfunc03(ARRAY[10, 20]);
+ cttestfunc03
+--------------
+ 10
+(1 row)
+
+select cttestfunc03(ARRAY[10.1, 20.1]);
+ cttestfunc03
+--------------
+ 10.1
+(1 row)
+
+select cttestfunc03(ARRAY[10, 20.1]);
+ cttestfunc03
+--------------
+ 10
+(1 row)
+
+select cttestfunc04(10, 20);
+ cttestfunc04
+--------------
+ 10
+(1 row)
+
+select cttestfunc04(10.1, 20.1);
+ cttestfunc04
+--------------
+ 10.1
+(1 row)
+
+select cttestfunc04(10, 20.1);
+ cttestfunc04
+--------------
+ 10
+(1 row)
+
+select cttestfunc05(10, 20);
+ cttestfunc05
+--------------
+ {10,20}
+(1 row)
+
+select cttestfunc05(10.1, 20.1);
+ cttestfunc05
+--------------
+ {10.1,20.1}
+(1 row)
+
+select cttestfunc05(10, 20.1);
+ cttestfunc05
+--------------
+ {10,20.1}
+(1 row)
+
+select cttestfunc06(1,1.1);
+ cttestfunc06
+--------------
+ {1,1.1}
+(1 row)
+
+select cttestfunc07(10, 20);
+ cttestfunc07
+--------------
+ {10,20}
+(1 row)
+
+select cttestfunc07(10.1, 20.1);
+ cttestfunc07
+--------------
+ {10.1,20.1}
+(1 row)
+
+select cttestfunc07(10, 20.1);
+ cttestfunc07
+--------------
+ {10,20.1}
+(1 row)
+
+select cttestfunc08(1,1.1);
+ cttestfunc08
+--------------
+ {1,1.1}
+(1 row)
+
+-- should to fail
+select cttestfunc06(array[10], array[2]);
+ERROR: function cttestfunc06(integer[], integer[]) does not exist
+LINE 1: select cttestfunc06(array[10], array[2]);
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index 0360667..566b25b 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -814,3 +814,91 @@ select * from dfview;
drop view dfview;
drop function dfunc(anyelement, anyelement, bool);
+
+create or replace function cttestfunc01(anycompatible, anycompatible)
+returns anycompatible as $$
+begin
+ if $1 > $2 then
+ return $1;
+ else
+ return $2;
+ end if;
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc02(anycompatible, anycompatible)
+returns anycompatiblearray as $$
+begin
+ return ARRAY[$1, $2];
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc03(anycompatiblearray)
+returns anycompatible as $$
+begin
+ return $1[1];
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc04(variadic anycompatiblearray)
+returns anycompatible as $$
+begin
+ return (select min(v) from unnest($1) g(v));
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc05(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+begin
+ return $1;
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc06(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+begin
+ return ARRAY[$1, $2];
+end;
+$$ language plpgsql;
+
+create or replace function cttestfunc07(variadic anycompatiblearray)
+returns anycompatiblearray as $$
+ select $1
+$$ language sql;
+
+create or replace function cttestfunc08(anycompatiblenonarray, anycompatiblenonarray)
+returns anycompatiblearray as $$
+select array[$1, $2]
+$$ language sql;
+
+
+select cttestfunc01(10, 20);
+select cttestfunc01(10.1, 20.1);
+select cttestfunc01(10, 20.1);
+
+select cttestfunc02(10, 20);
+select cttestfunc02(10.1, 20.1);
+select cttestfunc02(10, 20.1);
+
+select cttestfunc03(ARRAY[10, 20]);
+select cttestfunc03(ARRAY[10.1, 20.1]);
+select cttestfunc03(ARRAY[10, 20.1]);
+
+select cttestfunc04(10, 20);
+select cttestfunc04(10.1, 20.1);
+select cttestfunc04(10, 20.1);
+
+select cttestfunc05(10, 20);
+select cttestfunc05(10.1, 20.1);
+select cttestfunc05(10, 20.1);
+
+select cttestfunc06(1,1.1);
+
+select cttestfunc07(10, 20);
+select cttestfunc07(10.1, 20.1);
+select cttestfunc07(10, 20.1);
+
+select cttestfunc08(1,1.1);
+
+-- should to fail
+select cttestfunc06(array[10], array[2]);
В списке pgsql-hackers по дате отправления: