Commit 4dbcb3f8 authored by Tom Lane's avatar Tom Lane

Restructure polymorphic-type resolution in funcapi.c.

resolve_polymorphic_tupdesc() and resolve_polymorphic_argtypes() failed to
cover the case of having to resolve anyarray given only an anyrange input.
The bug was masked if anyelement was also used (as either input or
output), which probably helps account for our not having noticed.

While looking at this I noticed that resolve_generic_type() would produce
the wrong answer if asked to make that same resolution.  ISTM that
resolve_generic_type() is confusingly defined and overly complex, so
rather than fix it, let's just make funcapi.c do the specific lookups
it requires for itself.

With this change, resolve_generic_type() is not used anywhere, so remove
it in HEAD.  In the back branches, leave it alone (complete with bug)
just in case any external code is using it.

While we're here, make some other refactoring adjustments in funcapi.c
with an eye to upcoming future expansion of the set of polymorphic types:

* Simplify quick-exit tests by adding an overall have_polymorphic_result
flag.  This is about a wash now but will be a win when there are more
flags.

* Reduce duplication of code between resolve_polymorphic_tupdesc() and
resolve_polymorphic_argtypes().

* Don't bother to validate correct matching of anynonarray or anyenum;
the parser should have done that, and even if it didn't, just doing
"return false" here would lead to a very confusing, off-point error
message.  (Really, "return false" in these two functions should only
occur if the call_expr isn't supplied or we can't obtain data type
info from it.)

* For the same reason, throw an elog rather than "return false" if
we fail to resolve a polymorphic type.

The bug's been there since we added anyrange, so back-patch to
all supported branches.

Discussion: https://postgr.es/m/6093.1584202130@sss.pgh.pa.us
parent e83daa7e
......@@ -14,7 +14,6 @@
*/
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits.h"
......@@ -26,7 +25,6 @@
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
......@@ -1968,108 +1966,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return rettype;
}
/*
* resolve_generic_type()
* Deduce an individual actual datatype on the assumption that
* the rules for polymorphic types are being followed.
*
* declared_type is the declared datatype we want to resolve.
* context_actual_type is the actual input datatype to some argument
* that has declared datatype context_declared_type.
*
* If declared_type isn't polymorphic, we just return it. Otherwise,
* context_declared_type must be polymorphic, and we deduce the correct
* return type based on the relationship of the two polymorphic types.
*/
Oid
resolve_generic_type(Oid declared_type,
Oid context_actual_type,
Oid context_declared_type)
{
if (declared_type == ANYARRAYOID)
{
if (context_declared_type == ANYARRAYOID)
{
/*
* Use actual type, but it must be an array; or if it's a domain
* over array, use the base array type.
*/
Oid context_base_type = getBaseType(context_actual_type);
Oid array_typelem = get_element_type(context_base_type);
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(context_base_type))));
return context_base_type;
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID ||
context_declared_type == ANYRANGEOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
if (!OidIsValid(array_typeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(context_actual_type))));
return array_typeid;
}
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
declared_type == ANYENUMOID ||
declared_type == ANYRANGEOID)
{
if (context_declared_type == ANYARRAYOID)
{
/* Use the element type corresponding to actual type */
Oid context_base_type = getBaseType(context_actual_type);
Oid array_typelem = get_element_type(context_base_type);
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(context_base_type))));
return array_typelem;
}
else if (context_declared_type == ANYRANGEOID)
{
/* Use the element type corresponding to actual type */
Oid context_base_type = getBaseType(context_actual_type);
Oid range_typelem = get_range_subtype(context_base_type);
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(context_base_type))));
return range_typelem;
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
{
/* Use the actual type; it doesn't matter if array or not */
return context_actual_type;
}
}
else
{
/* declared_type isn't polymorphic, so return it as-is */
return declared_type;
}
/* If we get here, declared_type is polymorphic and context isn't */
/* NB: this is a calling-code logic error, not a user error */
elog(ERROR, "could not determine polymorphic type because context isn't polymorphic");
return InvalidOid; /* keep compiler quiet */
}
/* TypeCategory()
* Assign a category to the specified type OID.
......
This diff is collapsed.
......@@ -79,9 +79,6 @@ extern Oid enforce_generic_type_consistency(const Oid *actual_arg_types,
int nargs,
Oid rettype,
bool allow_poly);
extern Oid resolve_generic_type(Oid declared_type,
Oid context_actual_type,
Oid context_declared_type);
extern CoercionPathType find_coercion_pathway(Oid targetTypeId,
Oid sourceTypeId,
......
......@@ -1490,6 +1490,15 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
as $$ select array[lower($1), upper($1)], array[upper($1), lower($1)] $$
language sql;
select * from outparam2_succeed(int4range(1,11));
lu | ul
--------+--------
{1,11} | {11,1}
(1 row)
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
......@@ -1498,12 +1507,14 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
i | r
-----+--------
123 | [1,11)
create function table_succeed(r anyrange)
returns table(l anyelement, u anyelement)
as $$ select lower($1), upper($1) $$
language sql;
select * from table_succeed(int4range(1,11));
l | u
---+----
1 | 11
(1 row)
-- should fail
......
......@@ -517,15 +517,23 @@ create function outparam_succeed(i anyrange, out r anyrange, out t text)
select * from outparam_succeed(int4range(1,2));
create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
as $$ select array[lower($1), upper($1)], array[upper($1), lower($1)] $$
language sql;
select * from outparam2_succeed(int4range(1,11));
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
create function table_succeed(r anyrange)
returns table(l anyelement, u anyelement)
as $$ select lower($1), upper($1) $$
language sql;
select * from table_succeed(123, int4range(1,11));
select * from table_succeed(int4range(1,11));
-- should fail
create function outparam_fail(i anyelement, out r anyrange, out t text)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment