diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/parse_clause.c | 12 | ||||
-rw-r--r-- | src/backend/parser/parse_coerce.c | 315 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 5 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 11 |
4 files changed, 203 insertions, 140 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index a2cd7dccc15..b3a6b67f5c3 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.132 2004/06/09 19:08:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.133 2004/06/16 01:26:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -915,11 +915,11 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, * Insert coercion functions if needed. Note that a difference in * typmod can only happen if input has typmod but outcoltypmod is -1. * In that case we insert a RelabelType to clearly mark that result's - * typmod is not same as input. + * typmod is not same as input. We never need coerce_type_typmod. */ if (l_colvar->vartype != outcoltype) l_node = coerce_type(pstate, (Node *) l_colvar, l_colvar->vartype, - outcoltype, + outcoltype, outcoltypmod, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (l_colvar->vartypmod != outcoltypmod) l_node = (Node *) makeRelabelType((Expr *) l_colvar, @@ -930,7 +930,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, if (r_colvar->vartype != outcoltype) r_node = coerce_type(pstate, (Node *) r_colvar, r_colvar->vartype, - outcoltype, + outcoltype, outcoltypmod, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (r_colvar->vartypmod != outcoltypmod) r_node = (Node *) makeRelabelType((Expr *) r_colvar, @@ -1276,7 +1276,7 @@ transformGroupClause(ParseState *pstate, List *grouplist, if (restype == UNKNOWNOID) { tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, - restype, TEXTOID, + restype, TEXTOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); restype = tle->resdom->restype = TEXTOID; @@ -1528,7 +1528,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, if (restype == UNKNOWNOID && resolveUnknown) { tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, - restype, TEXTOID, + restype, TEXTOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); restype = tle->resdom->restype = TEXTOID; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index b12d1854aa8..3878d07d5a1 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.118 2004/06/06 00:41:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.119 2004/06/16 01:26:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -33,8 +33,13 @@ static Node *coerce_type_typmod(Node *node, - Oid targetTypeId, int32 targetTypMod, - CoercionForm cformat, bool isExplicit); + Oid targetTypeId, int32 targetTypMod, + CoercionForm cformat, bool isExplicit, + bool hideInputCoercion); +static void hide_coercion_node(Node *node); +static Node *build_coercion_expression(Node *node, Oid funcId, + Oid targetTypeId, int32 targetTypMod, + CoercionForm cformat, bool isExplicit); static Node *coerce_record_to_complex(ParseState *pstate, Node *node, Oid targetTypeId, CoercionContext ccontext, @@ -67,22 +72,27 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, CoercionContext ccontext, CoercionForm cformat) { - if (can_coerce_type(1, &exprtype, &targettype, ccontext)) - expr = coerce_type(pstate, expr, exprtype, targettype, - ccontext, cformat); - else - expr = NULL; + Node *result; + + if (!can_coerce_type(1, &exprtype, &targettype, ccontext)) + return NULL; + + result = coerce_type(pstate, expr, exprtype, + targettype, targettypmod, + ccontext, cformat); /* * If the target is a fixed-length type, it may need a length coercion - * as well as a type coercion. + * as well as a type coercion. If we find ourselves adding both, + * force the inner coercion node to implicit display form. */ - if (expr != NULL) - expr = coerce_type_typmod(expr, targettype, targettypmod, - cformat, - (cformat != COERCE_IMPLICIT_CAST)); + result = coerce_type_typmod(result, + targettype, targettypmod, + cformat, + (cformat != COERCE_IMPLICIT_CAST), + (result != expr && !IsA(result, Const))); - return expr; + return result; } @@ -93,10 +103,13 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, * The caller should already have determined that the coercion is possible; * see can_coerce_type. * - * No coercion to a typmod (length) is performed here. The caller must - * call coerce_type_typmod as well, if a typmod constraint is wanted. + * Normally, no coercion to a typmod (length) is performed here. The caller + * must call coerce_type_typmod as well, if a typmod constraint is wanted. * (But if the target type is a domain, it may internally contain a * typmod constraint, which will be applied inside coerce_to_domain.) + * In some cases pg_cast specifies a type coercion function that also + * applies length conversion, and in those cases only, the result will + * already be properly coerced to the specified typmod. * * pstate is only used in the case that we are able to resolve the type of * a previously UNKNOWN Param. It is okay to pass pstate = NULL if the @@ -104,7 +117,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, */ Node * coerce_type(ParseState *pstate, Node *node, - Oid inputTypeId, Oid targetTypeId, + Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat) { Node *result; @@ -178,7 +191,7 @@ coerce_type(ParseState *pstate, Node *node, /* If target is a domain, apply constraints. */ if (targetTyptype == 'd') result = coerce_to_domain(result, InvalidOid, targetTypeId, - cformat); + cformat, false); ReleaseSysCache(targetType); @@ -240,13 +253,14 @@ coerce_type(ParseState *pstate, Node *node, * Generate an expression tree representing run-time * application of the conversion function. If we are dealing * with a domain target type, the conversion function will - * yield the base type. + * yield the base type (and we assume targetTypeMod must be -1). */ Oid baseTypeId = getBaseType(targetTypeId); - result = (Node *) makeFuncExpr(funcId, baseTypeId, - list_make1(node), - cformat); + result = build_coercion_expression(node, funcId, + baseTypeId, targetTypeMod, + cformat, + (cformat != COERCE_IMPLICIT_CAST)); /* * If domain, coerce to the domain type and relabel with @@ -254,7 +268,7 @@ coerce_type(ParseState *pstate, Node *node, */ if (targetTypeId != baseTypeId) result = coerce_to_domain(result, baseTypeId, targetTypeId, - cformat); + cformat, true); } else { @@ -269,7 +283,7 @@ coerce_type(ParseState *pstate, Node *node, * then we won't need a RelabelType node. */ result = coerce_to_domain(node, InvalidOid, targetTypeId, - cformat); + cformat, false); if (result == node) { /* @@ -409,11 +423,13 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, * has not bothered to look this up) * 'typeId': target type to coerce to * 'cformat': coercion format + * 'hideInputCoercion': if true, hide the input coercion under this one. * * If the target type isn't a domain, the given 'arg' is returned as-is. */ Node * -coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat) +coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, + CoercionForm cformat, bool hideInputCoercion) { CoerceToDomain *result; int32 typmod; @@ -426,6 +442,10 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat) if (baseTypeId == typeId) return arg; + /* Suppress display of nested coercion steps */ + if (hideInputCoercion) + hide_coercion_node(arg); + /* * If the domain applies a typmod to its base type, build the * appropriate coercion step. Mark it implicit for display purposes, @@ -444,7 +464,8 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat) if (typmod >= 0) arg = coerce_type_typmod(arg, baseTypeId, typmod, COERCE_IMPLICIT_CAST, - (cformat != COERCE_IMPLICIT_CAST)); + (cformat != COERCE_IMPLICIT_CAST), + false); /* * Now build the domain coercion node. This represents run-time @@ -473,57 +494,142 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat) * The caller must have already ensured that the value is of the correct * type, typically by applying coerce_type. * + * cformat determines the display properties of the generated node (if any), + * while isExplicit may affect semantics. If hideInputCoercion is true + * *and* we generate a node, the input node is forced to IMPLICIT display + * form, so that only the typmod coercion node will be visible when + * displaying the expression. + * * NOTE: this does not need to work on domain types, because any typmod * coercion for a domain is considered to be part of the type coercion * needed to produce the domain value in the first place. So, no getBaseType. */ static Node * coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, - CoercionForm cformat, bool isExplicit) + CoercionForm cformat, bool isExplicit, + bool hideInputCoercion) { Oid funcId; - int nargs; /* * A negative typmod is assumed to mean that no coercion is wanted. + * Also, skip coercion if already done. */ if (targetTypMod < 0 || targetTypMod == exprTypmod(node)) return node; - funcId = find_typmod_coercion_function(targetTypeId, &nargs); + funcId = find_typmod_coercion_function(targetTypeId); if (OidIsValid(funcId)) { - List *args; - Const *cons; + /* Suppress display of nested coercion steps */ + if (hideInputCoercion) + hide_coercion_node(node); + + node = build_coercion_expression(node, funcId, + targetTypeId, targetTypMod, + cformat, isExplicit); + } - /* Pass given value, plus target typmod as an int4 constant */ + return node; +} + +/* + * Mark a coercion node as IMPLICIT so it will never be displayed by + * ruleutils.c. We use this when we generate a nest of coercion nodes + * to implement what is logically one conversion; the inner nodes are + * forced to IMPLICIT_CAST format. This does not change their semantics, + * only display behavior. + * + * It is caller error to call this on something that doesn't have a + * CoercionForm field. + */ +static void +hide_coercion_node(Node *node) +{ + if (IsA(node, FuncExpr)) + ((FuncExpr *) node)->funcformat = COERCE_IMPLICIT_CAST; + else if (IsA(node, RelabelType)) + ((RelabelType *) node)->relabelformat = COERCE_IMPLICIT_CAST; + else if (IsA(node, RowExpr)) + ((RowExpr *) node)->row_format = COERCE_IMPLICIT_CAST; + else if (IsA(node, CoerceToDomain)) + ((CoerceToDomain *) node)->coercionformat = COERCE_IMPLICIT_CAST; + else + elog(ERROR, "unsupported node type: %d", (int) nodeTag(node)); +} + +/* + * build_coercion_expression() + * Construct a function-call expression for applying a pg_cast entry. + * + * This is used for both type-coercion and length-coercion functions, + * since there is no difference in terms of the calling convention. + */ +static Node * +build_coercion_expression(Node *node, Oid funcId, + Oid targetTypeId, int32 targetTypMod, + CoercionForm cformat, bool isExplicit) +{ + HeapTuple tp; + Form_pg_proc procstruct; + int nargs; + List *args; + Const *cons; + + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcId), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcId); + procstruct = (Form_pg_proc) GETSTRUCT(tp); + + /* + * Asserts essentially check that function is a legal coercion function. + * We can't make the seemingly obvious tests on prorettype and + * proargtypes[0], because of various binary-compatibility cases. + */ + /* Assert(targetTypeId == procstruct->prorettype); */ + Assert(!procstruct->proretset); + Assert(!procstruct->proisagg); + nargs = procstruct->pronargs; + Assert(nargs >= 1 && nargs <= 3); + /* Assert(procstruct->proargtypes[0] == exprType(node)); */ + Assert(nargs < 2 || procstruct->proargtypes[1] == INT4OID); + Assert(nargs < 3 || procstruct->proargtypes[2] == BOOLOID); + + ReleaseSysCache(tp); + + args = list_make1(node); + + if (nargs >= 2) + { + /* Pass target typmod as an int4 constant */ cons = makeConst(INT4OID, sizeof(int32), Int32GetDatum(targetTypMod), false, true); - args = list_make2(node, cons); + args = lappend(args, cons); + } - if (nargs == 3) - { - /* Pass it a boolean isExplicit parameter, too */ - cons = makeConst(BOOLOID, - sizeof(bool), - BoolGetDatum(isExplicit), - false, - true); - - args = lappend(args, cons); - } + if (nargs == 3) + { + /* Pass it a boolean isExplicit parameter, too */ + cons = makeConst(BOOLOID, + sizeof(bool), + BoolGetDatum(isExplicit), + false, + true); - node = (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat); + args = lappend(args, cons); } - return node; + return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat); } + /* * coerce_record_to_complex * Coerce a RECORD to a specific composite type. @@ -803,7 +909,7 @@ coerce_to_common_type(ParseState *pstate, Node *node, if (inputTypeId == targetTypeId) return node; /* no work */ if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT)) - node = coerce_type(pstate, node, inputTypeId, targetTypeId, + node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else ereport(ERROR, @@ -1528,8 +1634,8 @@ 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, use array_type_coerce(). + * of array types. If so, and if the element types have a suitable + * cast, use array_type_coerce() or array_type_length_coerce(). */ Oid targetElemType; Oid sourceElemType; @@ -1541,7 +1647,23 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, if (find_coercion_pathway(targetElemType, sourceElemType, ccontext, &elemfuncid)) { - *funcid = F_ARRAY_TYPE_COERCE; + if (!OidIsValid(elemfuncid)) + { + /* binary-compatible element type conversion */ + *funcid = F_ARRAY_TYPE_COERCE; + } + else + { + /* does the function take a typmod arg? */ + Oid argtypes[FUNC_MAX_ARGS]; + int nargs; + + (void) get_func_signature(elemfuncid, argtypes, &nargs); + if (nargs > 1) + *funcid = F_ARRAY_TYPE_LENGTH_COERCE; + else + *funcid = F_ARRAY_TYPE_COERCE; + } result = true; } } @@ -1554,14 +1676,8 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, /* * find_typmod_coercion_function -- does the given type need length coercion? * - * If the target type possesses a function named for the type - * and having parameter signature (targettype, int4), we assume that - * the type requires coercion to its own length and that the said - * function should be invoked to do that. - * - * Alternatively, the length-coercing function may have the signature - * (targettype, int4, bool). On success, *nargs is set to report which - * signature we found. + * If the target type possesses a pg_cast function from itself to itself, + * it must need length coercion. * * "bpchar" (ie, char(N)) and "numeric" are examples of such types. * @@ -1569,23 +1685,15 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, * function associated directly with the array type, but instead look for * one associated with the element type. If one exists, we report * array_length_coerce() as the coercion function to use. - * - * This mechanism may seem pretty grotty and in need of replacement by - * something in pg_cast, but since typmod is only interesting for datatypes - * that have special handling in the grammar, there's not really much - * percentage in making it any easier to apply such coercions ... */ Oid -find_typmod_coercion_function(Oid typeId, int *nargs) +find_typmod_coercion_function(Oid typeId) { Oid funcid = InvalidOid; bool isArray = false; Type targetType; Form_pg_type typeForm; - char *typname; - Oid typnamespace; - Oid oid_array[FUNC_MAX_ARGS]; - HeapTuple ftup; + HeapTuple tuple; targetType = typeidType(typeId); typeForm = (Form_pg_type) GETSTRUCT(targetType); @@ -1597,79 +1705,30 @@ find_typmod_coercion_function(Oid typeId, int *nargs) { /* Yes, switch our attention to the element type */ typeId = typeForm->typelem; - ReleaseSysCache(targetType); - targetType = typeidType(typeId); - typeForm = (Form_pg_type) GETSTRUCT(targetType); isArray = true; } + ReleaseSysCache(targetType); - /* Function name is same as type internal name, and in same namespace */ - typname = NameStr(typeForm->typname); - typnamespace = typeForm->typnamespace; - - /* First look for parameters (type, int4) */ - MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid)); - oid_array[0] = typeId; - oid_array[1] = INT4OID; - *nargs = 2; - - ftup = SearchSysCache(PROCNAMENSP, - CStringGetDatum(typname), - Int16GetDatum(2), - PointerGetDatum(oid_array), - ObjectIdGetDatum(typnamespace)); - if (HeapTupleIsValid(ftup)) - { - Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup); - - /* Make sure the function's result type is as expected */ - if (pform->prorettype == typeId && !pform->proretset && - !pform->proisagg) - { - /* Okay to use it */ - funcid = HeapTupleGetOid(ftup); - } - ReleaseSysCache(ftup); - } + /* Look in pg_cast */ + tuple = SearchSysCache(CASTSOURCETARGET, + ObjectIdGetDatum(typeId), + ObjectIdGetDatum(typeId), + 0, 0); - if (!OidIsValid(funcid)) + if (HeapTupleIsValid(tuple)) { - /* Didn't find a function, so now try (type, int4, bool) */ - oid_array[2] = BOOLOID; - *nargs = 3; - - ftup = SearchSysCache(PROCNAMENSP, - CStringGetDatum(typname), - Int16GetDatum(3), - PointerGetDatum(oid_array), - ObjectIdGetDatum(typnamespace)); - if (HeapTupleIsValid(ftup)) - { - Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup); + Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); - /* Make sure the function's result type is as expected */ - if (pform->prorettype == typeId && !pform->proretset && - !pform->proisagg) - { - /* Okay to use it */ - funcid = HeapTupleGetOid(ftup); - } - ReleaseSysCache(ftup); - } + funcid = castForm->castfunc; + ReleaseSysCache(tuple); } - ReleaseSysCache(targetType); - /* * Now, if we did find a coercion function for an array element type, - * report array_length_coerce() as the function to use. We know it - * takes three arguments always. + * report array_length_coerce() as the function to use. */ if (isArray && OidIsValid(funcid)) - { funcid = F_ARRAY_LENGTH_COERCE; - *nargs = 3; - } return funcid; } diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 3b4ad7cf8a0..6df4547ba2d 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.173 2004/06/09 19:08:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.174 2004/06/16 01:26:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1578,6 +1578,9 @@ exprTypmod(Node *expr) * * If coercedTypmod is not NULL, the typmod is stored there if the expression * is a length-coercion function, else -1 is stored there. + * + * Note that a combined type-and-length coercion will be treated as a + * length coercion by this routine. */ bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 781c94f8c2f..d5d71b67afd 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.170 2004/05/30 23:40:35 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.171 2004/06/16 01:26:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -150,7 +150,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * these cases, so why duplicate code... */ return coerce_type(pstate, linitial(fargs), - actual_arg_types[0], rettype, + actual_arg_types[0], rettype, -1, COERCION_EXPLICIT, COERCE_EXPLICIT_CALL); } else if (fdresult == FUNCDETAIL_NORMAL) @@ -726,11 +726,12 @@ func_get_detail(List *funcname, { Oid sourceType = argtypes[0]; Node *arg1 = linitial(fargs); + Oid cfuncid; if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) || (find_coercion_pathway(targetType, sourceType, - COERCION_EXPLICIT, funcid) && - *funcid == InvalidOid)) + COERCION_EXPLICIT, &cfuncid) && + cfuncid == InvalidOid)) { /* Yup, it's a type coercion */ *funcid = InvalidOid; @@ -1122,7 +1123,7 @@ make_fn_arguments(ParseState *pstate, lfirst(current_fargs) = coerce_type(pstate, lfirst(current_fargs), actual_arg_types[i], - declared_arg_types[i], + declared_arg_types[i], -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); } |