*/
#include "postgres.h"
-#include "access/htup_details.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits.h"
#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"
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.
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "nodes/nodeFuncs.h"
-#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
+typedef struct polymorphic_actuals
+{
+ Oid anyelement_type; /* anyelement mapping, if known */
+ Oid anyarray_type; /* anyarray mapping, if known */
+ Oid anyrange_type; /* anyrange mapping, if known */
+} polymorphic_actuals;
+
static void shutdown_MultiFuncCall(Datum arg);
static TypeFuncClass internal_get_result_type(Oid funcid,
Node *call_expr,
ReturnSetInfo *rsinfo,
Oid *resultTypeId,
TupleDesc *resultTupleDesc);
+static void resolve_anyelement_from_others(polymorphic_actuals *actuals);
+static void resolve_anyarray_from_others(polymorphic_actuals *actuals);
+static void resolve_anyrange_from_others(polymorphic_actuals *actuals);
static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
oidvector *declared_args,
Node *call_expr);
return NULL;
}
+/*
+ * Resolve actual type of ANYELEMENT from other polymorphic inputs
+ *
+ * Note: the error cases here and in the sibling functions below are not
+ * really user-facing; they could only occur if the function signature is
+ * incorrect or the parser failed to enforce consistency of the actual
+ * argument types. Hence, we don't sweat too much over the error messages.
+ */
+static void
+resolve_anyelement_from_others(polymorphic_actuals *actuals)
+{
+ if (OidIsValid(actuals->anyarray_type))
+ {
+ /* Use the element type corresponding to actual type */
+ Oid array_base_type = getBaseType(actuals->anyarray_type);
+ Oid array_typelem = get_element_type(array_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(array_base_type))));
+ actuals->anyelement_type = array_typelem;
+ }
+ else if (OidIsValid(actuals->anyrange_type))
+ {
+ /* Use the element type corresponding to actual type */
+ Oid range_base_type = getBaseType(actuals->anyrange_type);
+ Oid range_typelem = get_range_subtype(range_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(range_base_type))));
+ actuals->anyelement_type = range_typelem;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
+}
+
+/*
+ * Resolve actual type of ANYARRAY from other polymorphic inputs
+ */
+static void
+resolve_anyarray_from_others(polymorphic_actuals *actuals)
+{
+ /* If we don't know ANYELEMENT, resolve that first */
+ if (!OidIsValid(actuals->anyelement_type))
+ resolve_anyelement_from_others(actuals);
+
+ if (OidIsValid(actuals->anyelement_type))
+ {
+ /* Use the array type corresponding to actual type */
+ Oid array_typeid = get_array_type(actuals->anyelement_type);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(actuals->anyelement_type))));
+ actuals->anyarray_type = array_typeid;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
+}
+
+/*
+ * Resolve actual type of ANYRANGE from other polymorphic inputs
+ */
+static void
+resolve_anyrange_from_others(polymorphic_actuals *actuals)
+{
+ /*
+ * We can't deduce a range type from other polymorphic inputs, because
+ * there may be multiple range types with the same subtype.
+ */
+ elog(ERROR, "could not determine polymorphic type");
+}
+
/*
* Given the result tuple descriptor for a function with OUT parameters,
- * replace any polymorphic columns (ANYELEMENT etc) with correct data types
- * deduced from the input arguments. Returns true if able to deduce all types,
- * false if not.
+ * replace any polymorphic column types (ANYELEMENT etc) in the tupdesc
+ * 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.
*/
static bool
resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
int natts = tupdesc->natts;
int nargs = declared_args->dim1;
+ bool have_polymorphic_result = false;
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
- bool have_anynonarray = false;
- bool have_anyenum = false;
- Oid anyelement_type = InvalidOid;
- Oid anyarray_type = InvalidOid;
- Oid anyrange_type = InvalidOid;
+ polymorphic_actuals poly_actuals;
Oid anycollation = InvalidOid;
int i;
switch (TupleDescAttr(tupdesc, i)->atttypid)
{
case ANYELEMENTOID:
+ case ANYNONARRAYOID:
+ case ANYENUMOID:
+ have_polymorphic_result = true;
have_anyelement_result = true;
break;
case ANYARRAYOID:
+ have_polymorphic_result = true;
have_anyarray_result = true;
break;
- case ANYNONARRAYOID:
- have_anyelement_result = true;
- have_anynonarray = true;
- break;
- case ANYENUMOID:
- have_anyelement_result = true;
- have_anyenum = true;
- break;
case ANYRANGEOID:
+ have_polymorphic_result = true;
have_anyrange_result = true;
break;
default:
break;
}
}
- if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ if (!have_polymorphic_result)
return true;
/*
if (!call_expr)
return false; /* no hope */
+ memset(&poly_actuals, 0, sizeof(poly_actuals));
+
for (i = 0; i < nargs; i++)
{
switch (declared_args->values[i])
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID:
- if (!OidIsValid(anyelement_type))
- anyelement_type = get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anyelement_type))
+ {
+ poly_actuals.anyelement_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anyelement_type))
+ return false;
+ }
break;
case ANYARRAYOID:
- if (!OidIsValid(anyarray_type))
- anyarray_type = get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anyarray_type))
+ {
+ poly_actuals.anyarray_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anyarray_type))
+ return false;
+ }
break;
case ANYRANGEOID:
- if (!OidIsValid(anyrange_type))
- anyrange_type = get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anyrange_type))
+ {
+ poly_actuals.anyrange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anyrange_type))
+ return false;
+ }
break;
default:
break;
}
}
- /* If nothing found, parser messed up */
- if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
- return false;
-
/* If needed, deduce one polymorphic type from others */
- if (have_anyelement_result && !OidIsValid(anyelement_type))
- {
- if (OidIsValid(anyarray_type))
- anyelement_type = resolve_generic_type(ANYELEMENTOID,
- anyarray_type,
- ANYARRAYOID);
- if (OidIsValid(anyrange_type))
- {
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
-
- /* check for inconsistent array and range results */
- if (OidIsValid(anyelement_type) && anyelement_type != subtype)
- return false;
- anyelement_type = subtype;
- }
- }
-
- if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
-
- /*
- * We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
- */
- if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ if (have_anyelement_result && !OidIsValid(poly_actuals.anyelement_type))
+ resolve_anyelement_from_others(&poly_actuals);
- /* Enforce ANYNONARRAY if needed */
- if (have_anynonarray && type_is_array(anyelement_type))
- return false;
+ if (have_anyarray_result && !OidIsValid(poly_actuals.anyarray_type))
+ resolve_anyarray_from_others(&poly_actuals);
- /* Enforce ANYENUM if needed */
- if (have_anyenum && !type_is_enum(anyelement_type))
- return false;
+ if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
+ resolve_anyrange_from_others(&poly_actuals);
/*
* Identify the collation to use for polymorphic OUT parameters. (It'll
* range types are not collatable, so any possible internal collation of a
* range type is not considered here.
*/
- if (OidIsValid(anyelement_type))
- anycollation = get_typcollation(anyelement_type);
- else if (OidIsValid(anyarray_type))
- anycollation = get_typcollation(anyarray_type);
+ 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))
{
case ANYENUMOID:
TupleDescInitEntry(tupdesc, i + 1,
NameStr(att->attname),
- anyelement_type,
+ poly_actuals.anyelement_type,
-1,
0);
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
case ANYARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
NameStr(att->attname),
- anyarray_type,
+ poly_actuals.anyarray_type,
-1,
0);
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
case ANYRANGEOID:
TupleDescInitEntry(tupdesc, i + 1,
NameStr(att->attname),
- anyrange_type,
+ poly_actuals.anyrange_type,
-1,
0);
/* no collation should be attached to a range type */
/*
* Given the declared argument types and modes for a function, replace any
- * polymorphic types (ANYELEMENT etc) with correct data types deduced from the
- * input arguments. Returns true if able to deduce all types, false if not.
+ * 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.
+ *
* This is the same logic as resolve_polymorphic_tupdesc, but with a different
- * argument representation.
+ * argument representation, and slightly different output responsibilities.
*
* argmodes may be NULL, in which case all arguments are assumed to be IN mode.
*/
resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
Node *call_expr)
{
+ bool have_polymorphic_result = false;
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
- Oid anyelement_type = InvalidOid;
- Oid anyarray_type = InvalidOid;
- Oid anyrange_type = InvalidOid;
+ polymorphic_actuals poly_actuals;
int inargno;
int i;
- /* First pass: resolve polymorphic inputs, check for outputs */
+ /*
+ * First pass: resolve polymorphic inputs, check for outputs. As in
+ * resolve_polymorphic_tupdesc, we rely on the parser to have enforced
+ * type consistency.
+ */
+ memset(&poly_actuals, 0, sizeof(poly_actuals));
inargno = 0;
for (i = 0; i < numargs; i++)
{
case ANYNONARRAYOID:
case ANYENUMOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
have_anyelement_result = true;
+ }
else
{
- if (!OidIsValid(anyelement_type))
+ if (!OidIsValid(poly_actuals.anyelement_type))
{
- anyelement_type = get_call_expr_argtype(call_expr,
- inargno);
- if (!OidIsValid(anyelement_type))
+ poly_actuals.anyelement_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(poly_actuals.anyelement_type))
return false;
}
- argtypes[i] = anyelement_type;
+ argtypes[i] = poly_actuals.anyelement_type;
}
break;
case ANYARRAYOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
have_anyarray_result = true;
+ }
else
{
- if (!OidIsValid(anyarray_type))
+ if (!OidIsValid(poly_actuals.anyarray_type))
{
- anyarray_type = get_call_expr_argtype(call_expr,
- inargno);
- if (!OidIsValid(anyarray_type))
+ poly_actuals.anyarray_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(poly_actuals.anyarray_type))
return false;
}
- argtypes[i] = anyarray_type;
+ argtypes[i] = poly_actuals.anyarray_type;
}
break;
case ANYRANGEOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
have_anyrange_result = true;
+ }
else
{
- if (!OidIsValid(anyrange_type))
+ if (!OidIsValid(poly_actuals.anyrange_type))
{
- anyrange_type = get_call_expr_argtype(call_expr,
- inargno);
- if (!OidIsValid(anyrange_type))
+ poly_actuals.anyrange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(poly_actuals.anyrange_type))
return false;
}
- argtypes[i] = anyrange_type;
+ argtypes[i] = poly_actuals.anyrange_type;
}
break;
default:
}
/* Done? */
- if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ if (!have_polymorphic_result)
return true;
- /* If no input polymorphics, parser messed up */
- if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
- return false;
-
/* If needed, deduce one polymorphic type from others */
- if (have_anyelement_result && !OidIsValid(anyelement_type))
- {
- if (OidIsValid(anyarray_type))
- anyelement_type = resolve_generic_type(ANYELEMENTOID,
- anyarray_type,
- ANYARRAYOID);
- if (OidIsValid(anyrange_type))
- {
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
-
- /* check for inconsistent array and range results */
- if (OidIsValid(anyelement_type) && anyelement_type != subtype)
- return false;
- anyelement_type = subtype;
- }
- }
-
- if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ if (have_anyelement_result && !OidIsValid(poly_actuals.anyelement_type))
+ resolve_anyelement_from_others(&poly_actuals);
- /*
- * We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
- */
- if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ if (have_anyarray_result && !OidIsValid(poly_actuals.anyarray_type))
+ resolve_anyarray_from_others(&poly_actuals);
- /* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
+ if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
+ resolve_anyrange_from_others(&poly_actuals);
/* And finally replace the output column types as needed */
for (i = 0; i < numargs; i++)
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID:
- argtypes[i] = anyelement_type;
+ argtypes[i] = poly_actuals.anyelement_type;
break;
case ANYARRAYOID:
- argtypes[i] = anyarray_type;
+ argtypes[i] = poly_actuals.anyarray_type;
break;
case ANYRANGEOID:
- argtypes[i] = anyrange_type;
+ argtypes[i] = poly_actuals.anyrange_type;
break;
default:
break;