diff options
Diffstat (limited to 'src/backend/parser/parse_coerce.c')
-rw-r--r-- | src/backend/parser/parse_coerce.c | 234 |
1 files changed, 156 insertions, 78 deletions
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index c232dee4a58..98cc6691124 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -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; } |