diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/parse_coerce.c | 234 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 6 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 72 |
3 files changed, 206 insertions, 106 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; } diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index a0f92f66a64..45107e43ace 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.217 2007/04/27 22:05:48 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.218 2007/06/05 21:31:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -265,6 +265,7 @@ transformExpr(ParseState *pstate, Node *expr) case T_FieldSelect: case T_FieldStore: case T_RelabelType: + case T_CoerceViaIO: case T_ArrayCoerceExpr: case T_ConvertRowtypeExpr: case T_CaseTestExpr: @@ -1806,6 +1807,9 @@ exprType(Node *expr) case T_RelabelType: type = ((RelabelType *) expr)->resulttype; break; + case T_CoerceViaIO: + type = ((CoerceViaIO *) expr)->resulttype; + break; case T_ArrayCoerceExpr: type = ((ArrayCoerceExpr *) expr)->resulttype; break; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 01593317b1f..5e7a1cccffa 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.195 2007/03/27 23:21:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.196 2007/06/05 21:31:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -160,7 +160,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, if (fdresult == FUNCDETAIL_COERCION) { /* - * We can do it as a trivial coercion. coerce_type can handle these + * We interpreted it as a type coercion. coerce_type can handle these * cases, so why duplicate code... */ return coerce_type(pstate, linitial(fargs), @@ -669,7 +669,7 @@ func_select_candidate(int nargs, * (exact match) is as quick as possible. * * If an exact match isn't found: - * 1) check for possible interpretation as a trivial type coercion + * 1) check for possible interpretation as a type coercion request * 2) get a vector of all possible input arg type arrays constructed * from the superclasses of the original input arg types * 3) get a list of all possible argument type arrays to the function @@ -720,29 +720,35 @@ func_get_detail(List *funcname, * If we didn't find an exact match, next consider the possibility * that this is really a type-coercion request: a single-argument * function call where the function name is a type name. If so, and - * if we can do the coercion trivially (no run-time function call - * needed), then go ahead and treat the "function call" as a coercion. + * if the coercion path is RELABELTYPE or COERCEVIAIO, then go ahead + * and treat the "function call" as a coercion. + * * This interpretation needs to be given higher priority than * interpretations involving a type coercion followed by a function * call, otherwise we can produce surprising results. For example, we - * want "text(varchar)" to be interpreted as a trivial coercion, not + * want "text(varchar)" to be interpreted as a simple coercion, not * as "text(name(varchar))" which the code below this point is * entirely capable of selecting. * - * "Trivial" coercions are ones that involve binary-compatible types - * and ones that are coercing a previously-unknown-type literal - * constant to a specific type. + * We also treat a coercion of a previously-unknown-type literal + * constant to a specific type this way. + * + * The reason we reject COERCION_PATH_FUNC here is that we expect the + * cast implementation function to be named after the target type. + * Thus the function will be found by normal lookup if appropriate. * - * The reason we can restrict our check to binary-compatible coercions - * here is that we expect non-binary-compatible coercions to have an - * implementation function named after the target type. That function - * will be found by normal lookup if appropriate. + * The reason we reject COERCION_PATH_ARRAYCOERCE is mainly that + * you can't write "foo[] (something)" as a function call. In theory + * someone might want to invoke it as "_foo (something)" but we have + * never supported that historically, so we can insist that people + * write it as a normal cast instead. Lack of historical support is + * also the reason for not considering composite-type casts here. * - * NB: it's important that this code stays in sync with what - * coerce_type can do, because the caller will try to apply - * coerce_type if we return FUNCDETAIL_COERCION. If we return that - * result for something coerce_type can't handle, we'll cause infinite - * recursion between this module and coerce_type! + * NB: it's important that this code does not exceed what coerce_type + * can do, because the caller will try to apply coerce_type if we + * return FUNCDETAIL_COERCION. If we return that result for something + * coerce_type can't handle, we'll cause infinite recursion between + * this module and coerce_type! */ if (nargs == 1 && fargs != NIL) { @@ -755,16 +761,28 @@ func_get_detail(List *funcname, { Oid sourceType = argtypes[0]; Node *arg1 = linitial(fargs); - Oid cfuncid; - bool arrayCoerce; - - if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) || - (find_coercion_pathway(targetType, sourceType, - COERCION_EXPLICIT, - &cfuncid, &arrayCoerce) && - cfuncid == InvalidOid && !arrayCoerce)) + bool iscoercion; + + if (sourceType == UNKNOWNOID && IsA(arg1, Const)) + { + /* always treat typename('literal') as coercion */ + iscoercion = true; + } + else + { + CoercionPathType cpathtype; + Oid cfuncid; + + cpathtype = find_coercion_pathway(targetType, sourceType, + COERCION_EXPLICIT, + &cfuncid); + iscoercion = (cpathtype == COERCION_PATH_RELABELTYPE || + cpathtype == COERCION_PATH_COERCEVIAIO); + } + + if (iscoercion) { - /* Yup, it's a trivial type coercion */ + /* Treat it as a type coercion */ *funcid = InvalidOid; *rettype = targetType; *retset = false; |