+
+ /* Get the element type based on the array type, if we have one */
+ if (OidIsValid(array_typeid))
+ {
+ if (array_typeid == ANYARRAYOID)
+ {
+ /* Special case for ANYARRAY input: okay iff no ANYELEMENT */
+ if (have_anyelement)
+ return false;
+ return true;
+ }
+
+ array_typelem = get_element_type(array_typeid);
+ if (!OidIsValid(array_typelem))
+ return false; /* should be an array, but isn't */
+
+ 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 */
+ return false;
+ }
+ }
+
+ /* Looks valid */
+ return true;
+}
+
+/*
+ * enforce_generic_type_consistency()
+ * Make sure a polymorphic function is legally callable, and
+ * deduce actual argument and result types.
+ *
+ * If ANYARRAY or ANYELEMENT is used for a function's arguments or
+ * return type, we make sure the actual data types are consistent with
+ * each other. The argument consistency rules are shown above for
+ * check_generic_type_consistency().
+ *
+ * If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT
+ * or ANYARRAY argument, we attempt to deduce the actual type it should
+ * have. If successful, we alter that position of declared_arg_types[]
+ * so that make_fn_arguments will coerce the literal to the right thing.
+ *
+ * Rules are applied to the function's return type (possibly altering it)
+ * if it is declared ANYARRAY or ANYELEMENT:
+ *
+ * 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
+ * argument's actual type as the function's return type.
+ * 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument
+ * is ANYELEMENT, use the actual type of the argument to determine
+ * the function's return type, i.e. the element type's corresponding
+ * array type.
+ * 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT,
+ * generate an ERROR. This condition is prevented by CREATE FUNCTION
+ * and is therefore not expected here.
+ * 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
+ * argument's actual type as the function's return type.
+ * 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any
+ * argument is ANYARRAY, use the actual type of the argument to determine
+ * the function's return type, i.e. the array type's corresponding
+ * element type.
+ * 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
+ * generate an ERROR. This condition is prevented by CREATE FUNCTION
+ * and is therefore not expected here.
+ */
+Oid
+enforce_generic_type_consistency(Oid *actual_arg_types,
+ Oid *declared_arg_types,
+ int nargs,
+ Oid rettype)
+{
+ int j;
+ bool have_generics = false;
+ bool have_unknowns = false;
+ Oid elem_typeid = InvalidOid;
+ Oid array_typeid = InvalidOid;
+ Oid array_typelem;
+ bool have_anyelement = (rettype == ANYELEMENTOID);
+
+ /*
+ * Loop through the arguments to see if we have any that are ANYARRAY or
+ * ANYELEMENT. If so, require the actual types to be self-consistent
+ */
+ for (j = 0; j < nargs; j++)
+ {
+ Oid actual_type = actual_arg_types[j];
+
+ if (declared_arg_types[j] == ANYELEMENTOID)
+ {
+ have_generics = have_anyelement = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anyelement\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(elem_typeid),
+ format_type_be(actual_type))));
+ elem_typeid = actual_type;
+ }
+ else if (declared_arg_types[j] == ANYARRAYOID)
+ {
+ have_generics = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (OidIsValid(array_typeid) && actual_type != array_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anyarray\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(array_typeid),
+ format_type_be(actual_type))));
+ array_typeid = actual_type;
+ }
+ }
+
+ /*
+ * Fast Track: if none of the arguments are ANYARRAY or ANYELEMENT, return
+ * the unmodified rettype.
+ */
+ if (!have_generics)
+ return rettype;
+
+ /* 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 = InvalidOid;
+ }
+ else
+ {
+ array_typelem = get_element_type(array_typeid);
+ if (!OidIsValid(array_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared \"anyarray\" is not an array but type %s",
+ 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 \"anyarray\" is not consistent with argument declared \"anyelement\""),
+ errdetail("%s versus %s",
+ format_type_be(array_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+ else if (!OidIsValid(elem_typeid))
+ {
+ /* Only way to get here is if all the generic args are UNKNOWN */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not determine anyarray/anyelement type because input has type \"unknown\"")));
+ }
+
+ /*
+ * If we had any unknown inputs, re-scan to assign correct types
+ */
+ if (have_unknowns)
+ {
+ for (j = 0; j < nargs; j++)
+ {
+ Oid actual_type = actual_arg_types[j];
+
+ if (actual_type != UNKNOWNOID)
+ continue;
+
+ if (declared_arg_types[j] == ANYELEMENTOID)
+ declared_arg_types[j] = elem_typeid;
+ else if (declared_arg_types[j] == ANYARRAYOID)
+ {
+ if (!OidIsValid(array_typeid))
+ {
+ array_typeid = get_array_type(elem_typeid);
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = array_typeid;
+ }
+ }
+ }
+
+ /* if we return ANYARRAYOID use the appropriate argument type */
+ if (rettype == ANYARRAYOID)
+ {
+ if (!OidIsValid(array_typeid))
+ {
+ array_typeid = get_array_type(elem_typeid);
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return array_typeid;
+ }
+
+ /* if we return ANYELEMENTOID use the appropriate argument type */
+ if (rettype == ANYELEMENTOID)
+ return elem_typeid;
+
+ /* we don't return a generic type; send back the original return type */
+ return rettype;
+}
+
+/*
+ * resolve_generic_type()
+ * Deduce an individual actual datatype on the assumption that
+ * the rules for ANYARRAY/ANYELEMENT 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 */
+ Oid array_typelem = get_element_type(context_actual_type);
+
+ if (!OidIsValid(array_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared \"anyarray\" is not an array but type %s",
+ format_type_be(context_actual_type))));
+ return context_actual_type;
+ }
+ else if (context_declared_type == ANYELEMENTOID)
+ {
+ /* 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)
+ {
+ if (context_declared_type == ANYARRAYOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid array_typelem = get_element_type(context_actual_type);
+
+ if (!OidIsValid(array_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared \"anyarray\" is not an array but type %s",
+ format_type_be(context_actual_type))));
+ return array_typelem;
+ }
+ else if (context_declared_type == ANYELEMENTOID)
+ {
+ /* 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 ANYARRAY/ANYELEMENT type because context isn't polymorphic");
+ return InvalidOid; /* keep compiler quiet */