1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-09 22:41:56 +03:00

Fix up text concatenation so that it accepts all the reasonable cases that

were accepted by prior Postgres releases.  This takes care of the loose end
left by the preceding patch to downgrade implicit casts-to-text.  To avoid
breaking desirable behavior for array concatenation, introduce a new
polymorphic pseudo-type "anynonarray" --- the added concatenation operators
are actually text || anynonarray and anynonarray || text.
This commit is contained in:
Tom Lane
2007-06-06 23:00:50 +00:00
parent 7dab4f75ca
commit 2d4db3675f
22 changed files with 301 additions and 109 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.154 2007/06/05 21:31:05 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.155 2007/06/06 23:00:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -133,6 +133,7 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYOID ||
targetTypeId == ANYELEMENTOID ||
targetTypeId == ANYNONARRAYOID ||
(targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID) ||
(targetTypeId == ANYENUMOID && inputTypeId != UNKNOWNOID))
{
@ -141,12 +142,12 @@ coerce_type(ParseState *pstate, Node *node,
*
* 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 or ANYELEMENT. This should be all right,
* since an UNKNOWN value is still a perfectly valid Datum. However
* an UNKNOWN value is definitely *not* an array, and so we mustn't
* accept it for ANYARRAY. (Instead, we will call anyarray_in below,
* which will produce an error.) Likewise, UNKNOWN input is no good
* for ANYENUM.
* function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be
* all right, since an UNKNOWN value is still a perfectly valid Datum.
* However an UNKNOWN value is definitely *not* an array, and so we
* mustn't accept it for ANYARRAY. (Instead, we will call anyarray_in
* below, which will produce an error.) Likewise, UNKNOWN input is no
* good for ANYENUM.
*
* NB: we do NOT want a RelabelType here.
*/
@ -1078,9 +1079,13 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* 4) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
* 5) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* 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.)
*
* If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT
* or ANYARRAY argument, assume it is okay.
* If we have UNKNOWN input (ie, an untyped literal) for any polymorphic
* argument, assume it is okay.
*
* If an input is of type ANYARRAY (ie, we know it's an array, but not
* what element type), we will accept it as a match to an argument declared
@ -1100,21 +1105,26 @@ check_generic_type_consistency(Oid *actual_arg_types,
Oid array_typeid = InvalidOid;
Oid array_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
/*
* 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
* Loop through the arguments to see if we have any that are polymorphic.
* If so, require the actual types to be consistent.
*/
for (j = 0; j < nargs; j++)
{
Oid decl_type = declared_arg_types[j];
Oid actual_type = actual_arg_types[j];
if (declared_arg_types[j] == ANYELEMENTOID ||
declared_arg_types[j] == ANYENUMOID)
if (decl_type == ANYELEMENTOID ||
decl_type == ANYNONARRAYOID ||
decl_type == ANYENUMOID)
{
have_anyelement = true;
if (declared_arg_types[j] == ANYENUMOID)
if (decl_type == ANYNONARRAYOID)
have_anynonarray = true;
else if (decl_type == ANYENUMOID)
have_anyenum = true;
if (actual_type == UNKNOWNOID)
continue;
@ -1122,7 +1132,7 @@ check_generic_type_consistency(Oid *actual_arg_types,
return false;
elem_typeid = actual_type;
}
else if (declared_arg_types[j] == ANYARRAYOID)
else if (decl_type == ANYARRAYOID)
{
if (actual_type == UNKNOWNOID)
continue;
@ -1161,6 +1171,13 @@ check_generic_type_consistency(Oid *actual_arg_types,
}
}
if (have_anynonarray)
{
/* require the element type to not be an array */
if (type_is_array(elem_typeid))
return false;
}
if (have_anyenum)
{
/* require the element type to be an enum */
@ -1177,7 +1194,7 @@ check_generic_type_consistency(Oid *actual_arg_types,
* Make sure a polymorphic function is legally callable, and
* deduce actual argument and result types.
*
* If ANYARRAY, ANYELEMENT, or ANYENUM is used for a function's arguments or
* If any polymorphic pseudotype is used in 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().
@ -1211,6 +1228,10 @@ check_generic_type_consistency(Oid *actual_arg_types,
* 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
* 8) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* 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.)
*/
Oid
enforce_generic_type_consistency(Oid *actual_arg_types,
@ -1225,22 +1246,28 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
Oid array_typeid = InvalidOid;
Oid array_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
/*
* 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
* Loop through the arguments to see if we have any that are polymorphic.
* If so, require the actual types to be consistent.
*/
for (j = 0; j < nargs; j++)
{
Oid decl_type = declared_arg_types[j];
Oid actual_type = actual_arg_types[j];
if (declared_arg_types[j] == ANYELEMENTOID ||
declared_arg_types[j] == ANYENUMOID)
if (decl_type == ANYELEMENTOID ||
decl_type == ANYNONARRAYOID ||
decl_type == ANYENUMOID)
{
have_generics = have_anyelement = true;
if (declared_arg_types[j] == ANYENUMOID)
if (decl_type == ANYNONARRAYOID)
have_anynonarray = true;
else if (decl_type == ANYENUMOID)
have_anyenum = true;
if (actual_type == UNKNOWNOID)
{
@ -1256,7 +1283,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
format_type_be(actual_type))));
elem_typeid = actual_type;
}
else if (declared_arg_types[j] == ANYARRAYOID)
else if (decl_type == ANYARRAYOID)
{
have_generics = true;
if (actual_type == UNKNOWNOID)
@ -1326,6 +1353,16 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
errmsg("could not determine polymorphic type because input has type \"unknown\"")));
}
if (have_anynonarray)
{
/* require the element type to not be an array */
if (type_is_array(elem_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type matched to anynonarray is an array type: %s",
format_type_be(elem_typeid))));
}
if (have_anyenum)
{
/* require the element type to be an enum */
@ -1343,15 +1380,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
{
for (j = 0; j < nargs; j++)
{
Oid decl_type = declared_arg_types[j];
Oid actual_type = actual_arg_types[j];
if (actual_type != UNKNOWNOID)
continue;
if (declared_arg_types[j] == ANYELEMENTOID ||
declared_arg_types[j] == ANYENUMOID)
if (decl_type == ANYELEMENTOID ||
decl_type == ANYNONARRAYOID ||
decl_type == ANYENUMOID)
declared_arg_types[j] = elem_typeid;
else if (declared_arg_types[j] == ANYARRAYOID)
else if (decl_type == ANYARRAYOID)
{
if (!OidIsValid(array_typeid))
{
@ -1383,7 +1422,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
}
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID || rettype == ANYENUMOID)
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID)
return elem_typeid;
/* we don't return a generic type; send back the original return type */
@ -1423,6 +1464,7 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
@ -1436,7 +1478,9 @@ resolve_generic_type(Oid declared_type,
return array_typeid;
}
}
else if (declared_type == ANYELEMENTOID || declared_type == ANYENUMOID)
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@ -1451,6 +1495,7 @@ resolve_generic_type(Oid declared_type,
return array_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 */
@ -1564,6 +1609,7 @@ TypeCategory(Oid inType)
case (INTERNALOID):
case (OPAQUEOID):
case (ANYELEMENTOID):
case (ANYNONARRAYOID):
case (ANYENUMOID):
result = GENERIC_TYPE;
break;
@ -1707,7 +1753,12 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
/* Also accept any array type as coercible to ANYARRAY */
if (targettype == ANYARRAYOID)
if (get_element_type(srctype) != InvalidOid)
if (type_is_array(srctype))
return true;
/* Also accept any non-array type as coercible to ANYNONARRAY */
if (targettype == ANYNONARRAYOID)
if (!type_is_array(srctype))
return true;
/* Also accept any enum type as coercible to ANYENUM */
@ -1863,22 +1914,6 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
}
}
/*
* If we still haven't found a possibility, check for enums,
* and retry looking for a cast to or from ANYENUM. But don't
* mistakenly conclude that ANYENUM-to-some-enum-type is a
* trivial cast.
*/
if (result == COERCION_PATH_NONE)
{
if (type_is_enum(sourceTypeId))
result = find_coercion_pathway(targetTypeId, ANYENUMOID,
ccontext, funcid);
else if (sourceTypeId != ANYENUMOID && type_is_enum(targetTypeId))
result = find_coercion_pathway(ANYENUMOID, sourceTypeId,
ccontext, funcid);
}
/*
* If we still haven't found a possibility, consider automatic casting
* using I/O functions. We allow assignment casts to textual types