diff options
Diffstat (limited to 'src/backend/parser/parse_coerce.c')
-rw-r--r-- | src/backend/parser/parse_coerce.c | 221 |
1 files changed, 124 insertions, 97 deletions
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 00ce2a9927f..fbc83870a5c 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.151 2007/03/17 00:11:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.152 2007/03/27 23:21:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,7 +36,8 @@ static Node *coerce_type_typmod(Node *node, CoercionForm cformat, bool isExplicit, bool hideInputCoercion); static void hide_coercion_node(Node *node); -static Node *build_coercion_expression(Node *node, Oid funcId, +static Node *build_coercion_expression(Node *node, + Oid funcId, bool arrayCoerce, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, bool isExplicit); static Node *coerce_record_to_complex(ParseState *pstate, Node *node, @@ -121,6 +122,7 @@ coerce_type(ParseState *pstate, Node *node, { Node *result; Oid funcId; + bool arrayCoerce; if (targetTypeId == inputTypeId || node == NULL) @@ -277,9 +279,9 @@ coerce_type(ParseState *pstate, Node *node, return (Node *) param; } if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext, - &funcId)) + &funcId, &arrayCoerce)) { - if (OidIsValid(funcId)) + if (OidIsValid(funcId) || arrayCoerce) { /* * Generate an expression tree representing run-time application @@ -294,7 +296,7 @@ coerce_type(ParseState *pstate, Node *node, baseTypeMod = targetTypeMod; baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); - result = build_coercion_expression(node, funcId, + result = build_coercion_expression(node, funcId, arrayCoerce, baseTypeId, baseTypeMod, cformat, (cformat != COERCE_IMPLICIT_CAST)); @@ -394,6 +396,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, Oid inputTypeId = input_typeids[i]; Oid targetTypeId = target_typeids[i]; Oid funcId; + bool arrayCoerce; /* no problem if same type */ if (inputTypeId == targetTypeId) @@ -423,7 +426,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, * both binary-compatible and coercion-function cases. */ if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext, - &funcId)) + &funcId, &arrayCoerce)) continue; /* @@ -564,6 +567,7 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, bool hideInputCoercion) { Oid funcId; + bool arrayCoerce; /* * A negative typmod is assumed to mean that no coercion is wanted. Also, @@ -572,15 +576,14 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, if (targetTypMod < 0 || targetTypMod == exprTypmod(node)) return node; - funcId = find_typmod_coercion_function(targetTypeId); - - if (OidIsValid(funcId)) + if (find_typmod_coercion_function(targetTypeId, + &funcId, &arrayCoerce)) { /* Suppress display of nested coercion steps */ if (hideInputCoercion) hide_coercion_node(node); - node = build_coercion_expression(node, funcId, + node = build_coercion_expression(node, funcId, arrayCoerce, targetTypeId, targetTypMod, cformat, isExplicit); } @@ -605,6 +608,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, ArrayCoerceExpr)) + ((ArrayCoerceExpr *) node)->coerceformat = COERCE_IMPLICIT_CAST; else if (IsA(node, ConvertRowtypeExpr)) ((ConvertRowtypeExpr *) node)->convertformat = COERCE_IMPLICIT_CAST; else if (IsA(node, RowExpr)) @@ -617,74 +622,106 @@ hide_coercion_node(Node *node) /* * build_coercion_expression() - * Construct a function-call expression for applying a pg_cast entry. + * Construct an expression tree for applying a pg_cast entry. * - * This is used for both type-coercion and length-coercion functions, + * This is used for both type-coercion and length-coercion operations, * since there is no difference in terms of the calling convention. */ static Node * -build_coercion_expression(Node *node, Oid funcId, +build_coercion_expression(Node *node, + Oid funcId, bool arrayCoerce, 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.values[0] == exprType(node)); */ - Assert(nargs < 2 || procstruct->proargtypes.values[1] == INT4OID); - Assert(nargs < 3 || procstruct->proargtypes.values[2] == BOOLOID); + int nargs = 0; - ReleaseSysCache(tp); + if (OidIsValid(funcId)) + { + HeapTuple tp; + Form_pg_proc procstruct; - args = list_make1(node); + 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); - if (nargs >= 2) - { - /* Pass target typmod as an int4 constant */ - cons = makeConst(INT4OID, - -1, - sizeof(int32), - Int32GetDatum(targetTypMod), - false, - true); - - args = lappend(args, cons); + /* + * 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 + * 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.values[0] == exprType(node)); */ + Assert(nargs < 2 || procstruct->proargtypes.values[1] == INT4OID); + Assert(nargs < 3 || procstruct->proargtypes.values[2] == BOOLOID); + + ReleaseSysCache(tp); } - if (nargs == 3) + if (arrayCoerce) { - /* Pass it a boolean isExplicit parameter, too */ - cons = makeConst(BOOLOID, - -1, - sizeof(bool), - BoolGetDatum(isExplicit), - false, - true); - - args = lappend(args, cons); + /* 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 + { + /* We build an ordinary FuncExpr with special arguments */ + List *args; + Const *cons; + + Assert(OidIsValid(funcId)); + + args = list_make1(node); - return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat); + if (nargs >= 2) + { + /* Pass target typmod as an int4 constant */ + cons = makeConst(INT4OID, + -1, + sizeof(int32), + Int32GetDatum(targetTypMod), + false, + true); + + args = lappend(args, cons); + } + + if (nargs == 3) + { + /* Pass it a boolean isExplicit parameter, too */ + cons = makeConst(BOOLOID, + -1, + sizeof(bool), + BoolGetDatum(isExplicit), + false, + true); + + args = lappend(args, cons); + } + + return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat); + } } @@ -1636,7 +1673,9 @@ IsBinaryCoercible(Oid srctype, Oid targettype) * * 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. + * 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). * * NOTE: *funcid == InvalidOid does not necessarily mean that no work is * needed to do the coercion; if the target is a domain then we may need to @@ -1646,12 +1685,13 @@ IsBinaryCoercible(Oid srctype, Oid targettype) bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, - Oid *funcid) + Oid *funcid, bool *arrayCoerce) { bool result = false; HeapTuple tuple; *funcid = InvalidOid; + *arrayCoerce = false; /* Perhaps the types are domains; if so, look at their base types */ if (OidIsValid(sourceTypeId)) @@ -1707,18 +1747,19 @@ 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() or array_type_length_coerce(). + * report that with arrayCoerce = true. * * Hack: disallow coercions to oidvector and int2vector, which * otherwise tend to capture coercions that should go to "real" array * types. We want those types to be considered "real" arrays for many - * purposes, but not this one. (Also, array_type_coerce isn't + * purposes, but not this one. (Also, ArrayCoerceExpr isn't * 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; @@ -1727,21 +1768,12 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, (sourceElemType = get_element_type(sourceTypeId)) != InvalidOid) { if (find_coercion_pathway(targetElemType, sourceElemType, - ccontext, &elemfuncid)) + ccontext, + &elemfuncid, &elemarraycoerce) && + !elemarraycoerce) { - if (!OidIsValid(elemfuncid)) - { - /* binary-compatible element type conversion */ - *funcid = F_ARRAY_TYPE_COERCE; - } - else - { - /* does the function take a typmod arg? */ - if (get_func_nargs(elemfuncid) > 1) - *funcid = F_ARRAY_TYPE_LENGTH_COERCE; - else - *funcid = F_ARRAY_TYPE_COERCE; - } + *funcid = elemfuncid; + *arrayCoerce = true; result = true; } } @@ -1761,18 +1793,20 @@ 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 - * array_length_coerce() as the coercion function to use. + * one associated with the element type. If one exists, we report it with + * *arrayCoerce set to true. */ -Oid -find_typmod_coercion_function(Oid typeId) +bool +find_typmod_coercion_function(Oid typeId, + Oid *funcid, bool *arrayCoerce) { - Oid funcid = InvalidOid; - bool isArray = false; Type targetType; Form_pg_type typeForm; HeapTuple tuple; + *funcid = InvalidOid; + *arrayCoerce = false; + targetType = typeidType(typeId); typeForm = (Form_pg_type) GETSTRUCT(targetType); @@ -1783,7 +1817,7 @@ find_typmod_coercion_function(Oid typeId) { /* Yes, switch our attention to the element type */ typeId = typeForm->typelem; - isArray = true; + *arrayCoerce = true; } ReleaseSysCache(targetType); @@ -1797,16 +1831,9 @@ find_typmod_coercion_function(Oid typeId) { Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); - funcid = castForm->castfunc; + *funcid = castForm->castfunc; ReleaseSysCache(tuple); } - /* - * Now, if we did find a coercion function for an array element type, - * report array_length_coerce() as the function to use. - */ - if (isArray && OidIsValid(funcid)) - funcid = F_ARRAY_LENGTH_COERCE; - - return funcid; + return OidIsValid(*funcid); } |