1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-11 10:01:57 +03:00

Downgrade implicit casts to text to be assignment-only, except for the ones

from the other string-category types; this eliminates a lot of surprising
interpretations that the parser could formerly make when there was no directly
applicable operator.

Create a general mechanism that supports casts to and from the standard string
types (text,varchar,bpchar) for *every* datatype, by invoking the datatype's
I/O functions.  These new casts are assignment-only in the to-string direction,
explicit-only in the other, and therefore should create no surprising behavior.
Remove a bunch of thereby-obsoleted datatype-specific casting functions.

The "general mechanism" is a new expression node type CoerceViaIO that can
actually convert between *any* two datatypes if their external text
representations are compatible.  This is more general than needed for the
immediate feature, but might be useful in plpgsql or other places in future.

This commit does nothing about the issue that applying the concatenation
operator || to non-text types will now fail, often with strange error messages
due to misinterpreting the operator as array concatenation.  Since it often
(not always) worked before, we should either make it succeed or at least give
a more user-friendly error; but details are still under debate.

Peter Eisentraut and Tom Lane
This commit is contained in:
Tom Lane
2007-06-05 21:31:09 +00:00
parent 1120b99445
commit 31edbadf4a
60 changed files with 850 additions and 1612 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.153 2007/04/02 03:49:38 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.154 2007/06/05 21:31:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -37,9 +37,10 @@ static Node *coerce_type_typmod(Node *node,
bool hideInputCoercion);
static void hide_coercion_node(Node *node);
static Node *build_coercion_expression(Node *node,
Oid funcId, bool arrayCoerce,
Oid targetTypeId, int32 targetTypMod,
CoercionForm cformat, bool isExplicit);
CoercionPathType pathtype,
Oid funcId,
Oid targetTypeId, int32 targetTypMod,
CoercionForm cformat, bool isExplicit);
static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
Oid targetTypeId,
CoercionContext ccontext,
@ -121,8 +122,8 @@ coerce_type(ParseState *pstate, Node *node,
CoercionContext ccontext, CoercionForm cformat)
{
Node *result;
CoercionPathType pathtype;
Oid funcId;
bool arrayCoerce;
if (targetTypeId == inputTypeId ||
node == NULL)
@ -280,10 +281,11 @@ coerce_type(ParseState *pstate, Node *node,
return (Node *) param;
}
if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId, &arrayCoerce))
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId);
if (pathtype != COERCION_PATH_NONE)
{
if (OidIsValid(funcId) || arrayCoerce)
if (pathtype != COERCION_PATH_RELABELTYPE)
{
/*
* Generate an expression tree representing run-time application
@ -298,7 +300,7 @@ coerce_type(ParseState *pstate, Node *node,
baseTypeMod = targetTypeMod;
baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
result = build_coercion_expression(node, funcId, arrayCoerce,
result = build_coercion_expression(node, pathtype, funcId,
baseTypeId, baseTypeMod,
cformat,
(cformat != COERCE_IMPLICIT_CAST));
@ -397,8 +399,8 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
{
Oid inputTypeId = input_typeids[i];
Oid targetTypeId = target_typeids[i];
CoercionPathType pathtype;
Oid funcId;
bool arrayCoerce;
/* no problem if same type */
if (inputTypeId == targetTypeId)
@ -426,8 +428,9 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
* If pg_cast shows that we can coerce, accept. This test now covers
* both binary-compatible and coercion-function cases.
*/
if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId, &arrayCoerce))
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId);
if (pathtype != COERCION_PATH_NONE)
continue;
/*
@ -567,8 +570,8 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
CoercionForm cformat, bool isExplicit,
bool hideInputCoercion)
{
CoercionPathType pathtype;
Oid funcId;
bool arrayCoerce;
/*
* A negative typmod is assumed to mean that no coercion is wanted. Also,
@ -577,14 +580,15 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
if (targetTypMod < 0 || targetTypMod == exprTypmod(node))
return node;
if (find_typmod_coercion_function(targetTypeId,
&funcId, &arrayCoerce))
pathtype = find_typmod_coercion_function(targetTypeId, &funcId);
if (pathtype != COERCION_PATH_NONE)
{
/* Suppress display of nested coercion steps */
if (hideInputCoercion)
hide_coercion_node(node);
node = build_coercion_expression(node, funcId, arrayCoerce,
node = build_coercion_expression(node, pathtype, funcId,
targetTypeId, targetTypMod,
cformat, isExplicit);
}
@ -609,6 +613,8 @@ hide_coercion_node(Node *node)
((FuncExpr *) node)->funcformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, RelabelType))
((RelabelType *) node)->relabelformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, CoerceViaIO))
((CoerceViaIO *) node)->coerceformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, ArrayCoerceExpr))
((ArrayCoerceExpr *) node)->coerceformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, ConvertRowtypeExpr))
@ -630,7 +636,8 @@ hide_coercion_node(Node *node)
*/
static Node *
build_coercion_expression(Node *node,
Oid funcId, bool arrayCoerce,
CoercionPathType pathtype,
Oid funcId,
Oid targetTypeId, int32 targetTypMod,
CoercionForm cformat, bool isExplicit)
{
@ -651,7 +658,7 @@ build_coercion_expression(Node *node,
/*
* These Asserts essentially check that function is a legal coercion
* function. We can't make the seemingly obvious tests on prorettype
* and proargtypes[0], even in the non-arrayCoerce case, because of
* and proargtypes[0], even in the COERCION_PATH_FUNC case, because of
* various binary-compatibility cases.
*/
/* Assert(targetTypeId == procstruct->prorettype); */
@ -666,26 +673,7 @@ build_coercion_expression(Node *node,
ReleaseSysCache(tp);
}
if (arrayCoerce)
{
/* We need to build an ArrayCoerceExpr */
ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr);
acoerce->arg = (Expr *) node;
acoerce->elemfuncid = funcId;
acoerce->resulttype = targetTypeId;
/*
* Label the output as having a particular typmod only if we are
* really invoking a length-coercion function, ie one with more
* than one argument.
*/
acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1;
acoerce->isExplicit = isExplicit;
acoerce->coerceformat = cformat;
return (Node *) acoerce;
}
else
if (pathtype == COERCION_PATH_FUNC)
{
/* We build an ordinary FuncExpr with special arguments */
List *args;
@ -723,6 +711,44 @@ build_coercion_expression(Node *node,
return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat);
}
else if (pathtype == COERCION_PATH_ARRAYCOERCE)
{
/* We need to build an ArrayCoerceExpr */
ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr);
acoerce->arg = (Expr *) node;
acoerce->elemfuncid = funcId;
acoerce->resulttype = targetTypeId;
/*
* Label the output as having a particular typmod only if we are
* really invoking a length-coercion function, ie one with more
* than one argument.
*/
acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1;
acoerce->isExplicit = isExplicit;
acoerce->coerceformat = cformat;
return (Node *) acoerce;
}
else if (pathtype == COERCION_PATH_COERCEVIAIO)
{
/* We need to build a CoerceViaIO node */
CoerceViaIO *iocoerce = makeNode(CoerceViaIO);
Assert(!OidIsValid(funcId));
iocoerce->arg = (Expr *) node;
iocoerce->resulttype = targetTypeId;
iocoerce->coerceformat = cformat;
return (Node *) iocoerce;
}
else
{
elog(ERROR, "unsupported pathtype %d in build_coercion_expression",
(int) pathtype);
return NULL; /* keep compiler quiet */
}
}
@ -1711,29 +1737,38 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
* find_coercion_pathway
* Look for a coercion pathway between two types.
*
* Currently, this deals only with scalar-type cases; it does not consider
* polymorphic types nor casts between composite types. (Perhaps fold
* those in someday?)
*
* ccontext determines the set of available casts.
*
* If we find a suitable entry in pg_cast, return TRUE, and set *funcid
* to the castfunc value, which may be InvalidOid for a binary-compatible
* coercion. Also, arrayCoerce is set to indicate whether this is a plain
* or array coercion (if true, funcid actually shows how to coerce the
* array elements).
* The possible result codes are:
* COERCION_PATH_NONE: failed to find any coercion pathway
* *funcid is set to InvalidOid
* COERCION_PATH_FUNC: apply the coercion function returned in *funcid
* COERCION_PATH_RELABELTYPE: binary-compatible cast, no function needed
* *funcid is set to InvalidOid
* COERCION_PATH_ARRAYCOERCE: need an ArrayCoerceExpr node
* *funcid is set to the element cast function, or InvalidOid
* if the array elements are binary-compatible
* COERCION_PATH_COERCEVIAIO: need a CoerceViaIO node
* *funcid is set to InvalidOid
*
* NOTE: *funcid == InvalidOid does not necessarily mean that no work is
* Note: COERCION_PATH_RELABELTYPE does not necessarily mean that no work is
* needed to do the coercion; if the target is a domain then we may need to
* apply domain constraint checking. If you want to check for a zero-effort
* conversion then use IsBinaryCoercible().
*/
bool
CoercionPathType
find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
CoercionContext ccontext,
Oid *funcid, bool *arrayCoerce)
Oid *funcid)
{
bool result = false;
CoercionPathType result = COERCION_PATH_NONE;
HeapTuple tuple;
*funcid = InvalidOid;
*arrayCoerce = false;
/* Perhaps the types are domains; if so, look at their base types */
if (OidIsValid(sourceTypeId))
@ -1743,7 +1778,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
/* Domains are always coercible to and from their base type */
if (sourceTypeId == targetTypeId)
return true;
return COERCION_PATH_RELABELTYPE;
/* Look in pg_cast */
tuple = SearchSysCache(CASTSOURCETARGET,
@ -1779,7 +1814,10 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
if (ccontext >= castcontext)
{
*funcid = castForm->castfunc;
result = true;
if (OidIsValid(*funcid))
result = COERCION_PATH_FUNC;
else
result = COERCION_PATH_RELABELTYPE;
}
ReleaseSysCache(tuple);
@ -1789,7 +1827,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
/*
* If there's no pg_cast entry, perhaps we are dealing with a pair of
* array types. If so, and if the element types have a suitable cast,
* report that with arrayCoerce = true.
* report that we can coerce with an ArrayCoerceExpr.
*
* Hack: disallow coercions to oidvector and int2vector, which
* otherwise tend to capture coercions that should go to "real" array
@ -1798,25 +1836,30 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
* guaranteed to produce an output that meets the restrictions of
* these datatypes, such as being 1-dimensional.)
*/
Oid targetElemType;
Oid sourceElemType;
Oid elemfuncid;
bool elemarraycoerce;
if (targetTypeId == OIDVECTOROID || targetTypeId == INT2VECTOROID)
return false;
if ((targetElemType = get_element_type(targetTypeId)) != InvalidOid &&
(sourceElemType = get_element_type(sourceTypeId)) != InvalidOid)
if (targetTypeId != OIDVECTOROID && targetTypeId != INT2VECTOROID)
{
if (find_coercion_pathway(targetElemType, sourceElemType,
ccontext,
&elemfuncid, &elemarraycoerce) &&
!elemarraycoerce)
Oid targetElem;
Oid sourceElem;
if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
(sourceElem = get_element_type(sourceTypeId)) != InvalidOid)
{
*funcid = elemfuncid;
*arrayCoerce = true;
result = true;
CoercionPathType elempathtype;
Oid elemfuncid;
elempathtype = find_coercion_pathway(targetElem,
sourceElem,
ccontext,
&elemfuncid);
if (elempathtype != COERCION_PATH_NONE &&
elempathtype != COERCION_PATH_ARRAYCOERCE)
{
*funcid = elemfuncid;
if (elempathtype == COERCION_PATH_COERCEVIAIO)
result = COERCION_PATH_COERCEVIAIO;
else
result = COERCION_PATH_ARRAYCOERCE;
}
}
}
@ -1826,14 +1869,39 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
* mistakenly conclude that ANYENUM-to-some-enum-type is a
* trivial cast.
*/
if (!result)
if (result == COERCION_PATH_NONE)
{
if (type_is_enum(sourceTypeId))
result = find_coercion_pathway(targetTypeId, ANYENUMOID,
ccontext, funcid, arrayCoerce);
ccontext, funcid);
else if (sourceTypeId != ANYENUMOID && type_is_enum(targetTypeId))
result = find_coercion_pathway(ANYENUMOID, sourceTypeId,
ccontext, funcid, arrayCoerce);
ccontext, funcid);
}
/*
* If we still haven't found a possibility, consider automatic casting
* using I/O functions. We allow assignment casts to textual types
* and explicit casts from textual types to be handled this way.
* (The CoerceViaIO mechanism is a lot more general than that, but
* this is all we want to allow in the absence of a pg_cast entry.)
* It would probably be better to insist on explicit casts in both
* directions, but this is a compromise to preserve something of the
* pre-8.3 behavior that many types had implicit (yipes!) casts to
* text.
*/
if (result == COERCION_PATH_NONE)
{
if (ccontext >= COERCION_ASSIGNMENT &&
(targetTypeId == TEXTOID ||
targetTypeId == VARCHAROID ||
targetTypeId == BPCHAROID))
result = COERCION_PATH_COERCEVIAIO;
else if (ccontext >= COERCION_EXPLICIT &&
(sourceTypeId == TEXTOID ||
sourceTypeId == VARCHAROID ||
sourceTypeId == BPCHAROID))
result = COERCION_PATH_COERCEVIAIO;
}
}
@ -1851,19 +1919,26 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
*
* If the given type is a varlena array type, we do not look for a coercion
* function associated directly with the array type, but instead look for
* one associated with the element type. If one exists, we report it with
* *arrayCoerce set to true.
* one associated with the element type. An ArrayCoerceExpr node must be
* used to apply such a function.
*
* We use the same result enum as find_coercion_pathway, but the only possible
* result codes are:
* COERCION_PATH_NONE: no length coercion needed
* COERCION_PATH_FUNC: apply the function returned in *funcid
* COERCION_PATH_ARRAYCOERCE: apply the function using ArrayCoerceExpr
*/
bool
CoercionPathType
find_typmod_coercion_function(Oid typeId,
Oid *funcid, bool *arrayCoerce)
Oid *funcid)
{
CoercionPathType result;
Type targetType;
Form_pg_type typeForm;
HeapTuple tuple;
*funcid = InvalidOid;
*arrayCoerce = false;
result = COERCION_PATH_FUNC;
targetType = typeidType(typeId);
typeForm = (Form_pg_type) GETSTRUCT(targetType);
@ -1875,7 +1950,7 @@ find_typmod_coercion_function(Oid typeId,
{
/* Yes, switch our attention to the element type */
typeId = typeForm->typelem;
*arrayCoerce = true;
result = COERCION_PATH_ARRAYCOERCE;
}
ReleaseSysCache(targetType);
@ -1893,5 +1968,8 @@ find_typmod_coercion_function(Oid typeId,
ReleaseSysCache(tuple);
}
return OidIsValid(*funcid);
if (!OidIsValid(*funcid))
result = COERCION_PATH_NONE;
return result;
}