diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 40591fd368..e1d5101ae1 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.64 2007/02/14 01:58:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.65 2007/03/27 23:21:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1315,6 +1315,17 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, relab->resulttype, 0, context->addrs); } + if (IsA(node, ArrayCoerceExpr)) + { + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; + + if (OidIsValid(acoerce->elemfuncid)) + add_object_address(OCLASS_PROC, acoerce->elemfuncid, 0, + context->addrs); + add_object_address(OCLASS_TYPE, acoerce->resulttype, 0, + context->addrs); + /* fall through to examine arguments */ + } if (IsA(node, ConvertRowtypeExpr)) { ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 784bbac231..94e6829f54 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.215 2007/02/27 23:48:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.216 2007/03/27 23:21:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -145,6 +145,9 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate, static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); /* ---------------------------------------------------------------- @@ -3501,6 +3504,83 @@ ExecEvalRelabelType(GenericExprState *exprstate, return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); } +/* ---------------------------------------------------------------- + * ExecEvalArrayCoerceExpr + * + * Evaluate an ArrayCoerceExpr node. + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) +{ + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr; + Datum result; + ArrayType *array; + FunctionCallInfoData locfcinfo; + + result = ExecEvalExpr(astate->arg, econtext, isNull, isDone); + + if (isDone && *isDone == ExprEndResult) + return result; /* nothing to do */ + if (*isNull) + return result; /* nothing to do */ + + /* + * If it's binary-compatible, modify the element type in the array header, + * but otherwise leave the array as we received it. + */ + if (!OidIsValid(acoerce->elemfuncid)) + { + /* Detoast input array if necessary, and copy in any case */ + array = DatumGetArrayTypePCopy(result); + ARR_ELEMTYPE(array) = astate->resultelemtype; + PG_RETURN_ARRAYTYPE_P(array); + } + + /* Detoast input array if necessary, but don't make a useless copy */ + array = DatumGetArrayTypeP(result); + + /* Initialize function cache if first time through */ + if (astate->elemfunc.fn_oid == InvalidOid) + { + AclResult aclresult; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(), + ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(acoerce->elemfuncid)); + + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), + econtext->ecxt_per_query_memory); + + /* Initialize additional info */ + astate->elemfunc.fn_expr = (Node *) acoerce; + } + + /* + * Use array_map to apply the function to each array element. + * + * We pass on the desttypmod and isExplicit flags whether or not the + * function wants them. + */ + InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3, + NULL, NULL); + locfcinfo.arg[0] = PointerGetDatum(array); + locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod); + locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit); + locfcinfo.argnull[0] = false; + locfcinfo.argnull[1] = false; + locfcinfo.argnull[2] = false; + + return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype, + astate->amstate); +} + /* * ExecEvalExprSwitchContext @@ -3770,6 +3850,26 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) gstate; } break; + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; + ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState); + + astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr; + astate->arg = ExecInitExpr(acoerce->arg, parent); + astate->resultelemtype = get_element_type(acoerce->resulttype); + if (astate->resultelemtype == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("target type is not an array"))); + /* Arrays over domains aren't supported yet */ + Assert(getBaseType(astate->resultelemtype) == + astate->resultelemtype); + astate->elemfunc.fn_oid = InvalidOid; /* not initialized */ + astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState)); + state = (ExprState *) astate; + } + break; case T_ConvertRowtypeExpr: { ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index f5dcc5b274..198a583f88 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.371 2007/03/17 00:11:03 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.372 2007/03/27 23:21:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1020,6 +1020,24 @@ _copyRelabelType(RelabelType *from) return newnode; } +/* + * _copyArrayCoerceExpr + */ +static ArrayCoerceExpr * +_copyArrayCoerceExpr(ArrayCoerceExpr *from) +{ + ArrayCoerceExpr *newnode = makeNode(ArrayCoerceExpr); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(elemfuncid); + COPY_SCALAR_FIELD(resulttype); + COPY_SCALAR_FIELD(resulttypmod); + COPY_SCALAR_FIELD(isExplicit); + COPY_SCALAR_FIELD(coerceformat); + + return newnode; +} + /* * _copyConvertRowtypeExpr */ @@ -3067,6 +3085,9 @@ copyObject(void *from) case T_RelabelType: retval = _copyRelabelType(from); break; + case T_ArrayCoerceExpr: + retval = _copyArrayCoerceExpr(from); + break; case T_ConvertRowtypeExpr: retval = _copyConvertRowtypeExpr(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 5863977611..977f121bc4 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.302 2007/03/17 00:11:03 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.303 2007/03/27 23:21:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -359,6 +359,27 @@ _equalRelabelType(RelabelType *a, RelabelType *b) return true; } +static bool +_equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(elemfuncid); + COMPARE_SCALAR_FIELD(resulttype); + COMPARE_SCALAR_FIELD(resulttypmod); + COMPARE_SCALAR_FIELD(isExplicit); + + /* + * Special-case COERCE_DONTCARE, so that planner can build coercion nodes + * that are equal() to both explicit and implicit coercions. + */ + if (a->coerceformat != b->coerceformat && + a->coerceformat != COERCE_DONTCARE && + b->coerceformat != COERCE_DONTCARE) + return false; + + return true; +} + static bool _equalConvertRowtypeExpr(ConvertRowtypeExpr *a, ConvertRowtypeExpr *b) { @@ -2013,6 +2034,9 @@ equal(void *a, void *b) case T_RelabelType: retval = _equalRelabelType(a, b); break; + case T_ArrayCoerceExpr: + retval = _equalArrayCoerceExpr(a, b); + break; case T_ConvertRowtypeExpr: retval = _equalConvertRowtypeExpr(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 083f016cf8..49202f8664 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.304 2007/03/17 00:11:03 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.305 2007/03/27 23:21:09 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -869,6 +869,19 @@ _outRelabelType(StringInfo str, RelabelType *node) WRITE_ENUM_FIELD(relabelformat, CoercionForm); } +static void +_outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node) +{ + WRITE_NODE_TYPE("ARRAYCOERCEEXPR"); + + WRITE_NODE_FIELD(arg); + WRITE_OID_FIELD(elemfuncid); + WRITE_OID_FIELD(resulttype); + WRITE_INT_FIELD(resulttypmod); + WRITE_BOOL_FIELD(isExplicit); + WRITE_ENUM_FIELD(coerceformat, CoercionForm); +} + static void _outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node) { @@ -2149,6 +2162,9 @@ _outNode(StringInfo str, void *obj) case T_RelabelType: _outRelabelType(str, obj); break; + case T_ArrayCoerceExpr: + _outArrayCoerceExpr(str, obj); + break; case T_ConvertRowtypeExpr: _outConvertRowtypeExpr(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 70612c864f..1f3b81e275 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.204 2007/03/17 00:11:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.205 2007/03/27 23:21:09 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -584,6 +584,24 @@ _readRelabelType(void) READ_DONE(); } +/* + * _readArrayCoerceExpr + */ +static ArrayCoerceExpr * +_readArrayCoerceExpr(void) +{ + READ_LOCALS(ArrayCoerceExpr); + + READ_NODE_FIELD(arg); + READ_OID_FIELD(elemfuncid); + READ_OID_FIELD(resulttype); + READ_INT_FIELD(resulttypmod); + READ_BOOL_FIELD(isExplicit); + READ_ENUM_FIELD(coerceformat, CoercionForm); + + READ_DONE(); +} + /* * _readConvertRowtypeExpr */ @@ -1024,6 +1042,8 @@ parseNodeString(void) return_value = _readFieldStore(); else if (MATCH("RELABELTYPE", 11)) return_value = _readRelabelType(); + else if (MATCH("ARRAYCOERCEEXPR", 15)) + return_value = _readArrayCoerceExpr(); else if (MATCH("CONVERTROWTYPEEXPR", 18)) return_value = _readConvertRowtypeExpr(); else if (MATCH("CASE", 4)) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 3dbb3bd802..ff5bb78337 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -54,7 +54,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.178 2007/02/22 22:00:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.179 2007/03/27 23:21:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1891,6 +1891,15 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) context->total.per_tuple += get_func_cost(saop->opfuncid) * cpu_operator_cost * estimate_array_length(arraynode) * 0.5; } + else if (IsA(node, ArrayCoerceExpr)) + { + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; + Node *arraynode = (Node *) acoerce->arg; + + if (OidIsValid(acoerce->elemfuncid)) + context->total.per_tuple += get_func_cost(acoerce->elemfuncid) * + cpu_operator_cost * estimate_array_length(arraynode); + } else if (IsA(node, RowCompareExpr)) { /* Conservatively assume we will check all the columns */ diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 1fa45e02a9..67652fbfde 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.239 2007/03/17 00:11:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.240 2007/03/27 23:21:09 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -710,7 +710,7 @@ contain_mutable_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, OpExpr)) + else if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; @@ -718,7 +718,7 @@ contain_mutable_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, DistinctExpr)) + else if (IsA(node, DistinctExpr)) { DistinctExpr *expr = (DistinctExpr *) node; @@ -726,7 +726,7 @@ contain_mutable_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, ScalarArrayOpExpr)) + else if (IsA(node, ScalarArrayOpExpr)) { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; @@ -734,7 +734,16 @@ contain_mutable_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, NullIfExpr)) + else if (IsA(node, ArrayCoerceExpr)) + { + ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; + + if (OidIsValid(expr->elemfuncid) && + func_volatile(expr->elemfuncid) != PROVOLATILE_IMMUTABLE) + return true; + /* else fall through to check args */ + } + else if (IsA(node, NullIfExpr)) { NullIfExpr *expr = (NullIfExpr *) node; @@ -742,7 +751,7 @@ contain_mutable_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, RowCompareExpr)) + else if (IsA(node, RowCompareExpr)) { RowCompareExpr *rcexpr = (RowCompareExpr *) node; ListCell *opid; @@ -793,7 +802,7 @@ contain_volatile_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, OpExpr)) + else if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; @@ -801,7 +810,7 @@ contain_volatile_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, DistinctExpr)) + else if (IsA(node, DistinctExpr)) { DistinctExpr *expr = (DistinctExpr *) node; @@ -809,7 +818,7 @@ contain_volatile_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, ScalarArrayOpExpr)) + else if (IsA(node, ScalarArrayOpExpr)) { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; @@ -817,7 +826,16 @@ contain_volatile_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, NullIfExpr)) + else if (IsA(node, ArrayCoerceExpr)) + { + ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; + + if (OidIsValid(expr->elemfuncid) && + func_volatile(expr->elemfuncid) == PROVOLATILE_VOLATILE) + return true; + /* else fall through to check args */ + } + else if (IsA(node, NullIfExpr)) { NullIfExpr *expr = (NullIfExpr *) node; @@ -825,7 +843,7 @@ contain_volatile_functions_walker(Node *node, void *context) return true; /* else fall through to check args */ } - if (IsA(node, RowCompareExpr)) + else if (IsA(node, RowCompareExpr)) { /* RowCompare probably can't have volatile ops, but check anyway */ RowCompareExpr *rcexpr = (RowCompareExpr *) node; @@ -932,6 +950,7 @@ contain_nonstrict_functions_walker(Node *node, void *context) } if (IsA(node, SubPlan)) return true; + /* ArrayCoerceExpr is strict at the array level, regardless of elemfunc */ if (IsA(node, FieldStore)) return true; if (IsA(node, CaseExpr)) @@ -1105,6 +1124,13 @@ find_nonnullable_rels_walker(Node *node, bool top_level) result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); } + else if (IsA(node, ArrayCoerceExpr)) + { + /* ArrayCoerceExpr is strict at the array level */ + ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; + + result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); + } else if (IsA(node, ConvertRowtypeExpr)) { /* not clear this is useful, but it can't hurt */ @@ -1460,6 +1486,13 @@ strip_implicit_coercions(Node *node) if (r->relabelformat == COERCE_IMPLICIT_CAST) return strip_implicit_coercions((Node *) r->arg); } + else if (IsA(node, ArrayCoerceExpr)) + { + ArrayCoerceExpr *c = (ArrayCoerceExpr *) node; + + if (c->coerceformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions((Node *) c->arg); + } else if (IsA(node, ConvertRowtypeExpr)) { ConvertRowtypeExpr *c = (ConvertRowtypeExpr *) node; @@ -1504,6 +1537,8 @@ set_coercionform_dontcare_walker(Node *node, void *context) ((FuncExpr *) node)->funcformat = COERCE_DONTCARE; else if (IsA(node, RelabelType)) ((RelabelType *) node)->relabelformat = COERCE_DONTCARE; + else if (IsA(node, ArrayCoerceExpr)) + ((ArrayCoerceExpr *) node)->coerceformat = COERCE_DONTCARE; else if (IsA(node, ConvertRowtypeExpr)) ((ConvertRowtypeExpr *) node)->convertformat = COERCE_DONTCARE; else if (IsA(node, RowExpr)) @@ -3436,6 +3471,8 @@ expression_tree_walker(Node *node, break; case T_RelabelType: return walker(((RelabelType *) node)->arg, context); + case T_ArrayCoerceExpr: + return walker(((ArrayCoerceExpr *) node)->arg, context); case T_ConvertRowtypeExpr: return walker(((ConvertRowtypeExpr *) node)->arg, context); case T_CaseExpr: @@ -3901,6 +3938,16 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; + ArrayCoerceExpr *newnode; + + FLATCOPY(newnode, acoerce, ArrayCoerceExpr); + MUTATE(newnode->arg, acoerce->arg, Expr *); + return (Node *) newnode; + } + break; case T_ConvertRowtypeExpr: { ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 00ce2a9927..fbc83870a5 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; + int nargs = 0; - 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); - - ReleaseSysCache(tp); - - args = list_make1(node); - - if (nargs >= 2) + if (OidIsValid(funcId)) { - /* Pass target typmod as an int4 constant */ - cons = makeConst(INT4OID, - -1, - sizeof(int32), - Int32GetDatum(targetTypMod), - false, - true); + HeapTuple tp; + Form_pg_proc procstruct; - args = lappend(args, 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); + + /* + * 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); + /* We need to build an ArrayCoerceExpr */ + ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr); - args = lappend(args, cons); + 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; - return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat); + Assert(OidIsValid(funcId)); + + args = list_make1(node); + + 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); } diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 9f0a501dc5..45e488e33f 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.214 2007/03/17 01:15:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.215 2007/03/27 23:21:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -265,6 +265,7 @@ transformExpr(ParseState *pstate, Node *expr) case T_FieldSelect: case T_FieldStore: case T_RelabelType: + case T_ArrayCoerceExpr: case T_ConvertRowtypeExpr: case T_CaseTestExpr: case T_CoerceToDomain: @@ -1804,6 +1805,9 @@ exprType(Node *expr) case T_RelabelType: type = ((RelabelType *) expr)->resulttype; break; + case T_ArrayCoerceExpr: + type = ((ArrayCoerceExpr *) expr)->resulttype; + break; case T_ConvertRowtypeExpr: type = ((ConvertRowtypeExpr *) expr)->resulttype; break; @@ -1918,6 +1922,8 @@ exprTypmod(Node *expr) return ((FieldSelect *) expr)->resulttypmod; case T_RelabelType: return ((RelabelType *) expr)->resulttypmod; + case T_ArrayCoerceExpr: + return ((ArrayCoerceExpr *) expr)->resulttypmod; case T_CaseExpr: { /* @@ -2072,47 +2078,68 @@ exprTypmod(Node *expr) bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) { - FuncExpr *func; - int nargs; - Const *second_arg; - if (coercedTypmod != NULL) *coercedTypmod = -1; /* default result on failure */ - /* Is it a function-call at all? */ - if (expr == NULL || !IsA(expr, FuncExpr)) - return false; - func = (FuncExpr *) expr; - /* - * If it didn't come from a coercion context, reject. + * Scalar-type length coercions are FuncExprs, array-type length + * coercions are ArrayCoerceExprs */ - if (func->funcformat != COERCE_EXPLICIT_CAST && - func->funcformat != COERCE_IMPLICIT_CAST) - return false; + if (expr && IsA(expr, FuncExpr)) + { + FuncExpr *func = (FuncExpr *) expr; + int nargs; + Const *second_arg; - /* - * If it's not a two-argument or three-argument function with the second - * argument being an int4 constant, it can't have been created from a - * length coercion (it must be a type coercion, instead). - */ - nargs = list_length(func->args); - if (nargs < 2 || nargs > 3) - return false; + /* + * If it didn't come from a coercion context, reject. + */ + if (func->funcformat != COERCE_EXPLICIT_CAST && + func->funcformat != COERCE_IMPLICIT_CAST) + return false; - second_arg = (Const *) lsecond(func->args); - if (!IsA(second_arg, Const) || - second_arg->consttype != INT4OID || - second_arg->constisnull) - return false; + /* + * If it's not a two-argument or three-argument function with the + * second argument being an int4 constant, it can't have been created + * from a length coercion (it must be a type coercion, instead). + */ + nargs = list_length(func->args); + if (nargs < 2 || nargs > 3) + return false; - /* - * OK, it is indeed a length-coercion function. - */ - if (coercedTypmod != NULL) - *coercedTypmod = DatumGetInt32(second_arg->constvalue); + second_arg = (Const *) lsecond(func->args); + if (!IsA(second_arg, Const) || + second_arg->consttype != INT4OID || + second_arg->constisnull) + return false; - return true; + /* + * OK, it is indeed a length-coercion function. + */ + if (coercedTypmod != NULL) + *coercedTypmod = DatumGetInt32(second_arg->constvalue); + + return true; + } + + if (expr && IsA(expr, ArrayCoerceExpr)) + { + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) expr; + + /* It's not a length coercion unless there's a nondefault typmod */ + if (acoerce->resulttypmod < 0) + return false; + + /* + * OK, it is indeed a length-coercion expression. + */ + if (coercedTypmod != NULL) + *coercedTypmod = acoerce->resulttypmod; + + return true; + } + + return false; } /* diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 888dc200d1..01593317b1 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.194 2007/02/01 19:10:27 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.195 2007/03/27 23:21:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -756,13 +756,15 @@ 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) && - cfuncid == InvalidOid)) + COERCION_EXPLICIT, + &cfuncid, &arrayCoerce) && + cfuncid == InvalidOid && !arrayCoerce)) { - /* Yup, it's a type coercion */ + /* Yup, it's a trivial type coercion */ *funcid = InvalidOid; *rettype = targetType; *retset = false; diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 43acdffcaf..38a86452e3 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.137 2007/02/27 23:48:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.138 2007/03/27 23:21:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -95,10 +95,6 @@ static void array_insert_slice(ArrayType *destArray, ArrayType *origArray, int *st, int *endp, int typlen, bool typbyval, char typalign); static int array_cmp(FunctionCallInfo fcinfo); -static Datum array_type_length_coerce_internal(ArrayType *src, - int32 desttypmod, - bool isExplicit, - FmgrInfo *fmgr_info); /* @@ -4093,222 +4089,6 @@ array_insert_slice(ArrayType *destArray, orignitems - orig_offset); } -/* - * array_type_coerce -- allow explicit or assignment coercion from - * one array type to another. - * - * array_type_length_coerce -- the same, for cases where both type and length - * coercion are done by a single function on the element type. - * - * Caller should have already verified that the source element type can be - * coerced into the target element type. - */ -Datum -array_type_coerce(PG_FUNCTION_ARGS) -{ - ArrayType *src = PG_GETARG_ARRAYTYPE_P(0); - FmgrInfo *fmgr_info = fcinfo->flinfo; - - return array_type_length_coerce_internal(src, -1, false, fmgr_info); -} - -Datum -array_type_length_coerce(PG_FUNCTION_ARGS) -{ - ArrayType *src = PG_GETARG_ARRAYTYPE_P(0); - int32 desttypmod = PG_GETARG_INT32(1); - bool isExplicit = PG_GETARG_BOOL(2); - FmgrInfo *fmgr_info = fcinfo->flinfo; - - return array_type_length_coerce_internal(src, desttypmod, - isExplicit, fmgr_info); -} - -static Datum -array_type_length_coerce_internal(ArrayType *src, - int32 desttypmod, - bool isExplicit, - FmgrInfo *fmgr_info) -{ - Oid src_elem_type = ARR_ELEMTYPE(src); - typedef struct - { - Oid srctype; - Oid desttype; - FmgrInfo coerce_finfo; - ArrayMapState amstate; - } atc_extra; - atc_extra *my_extra; - FunctionCallInfoData locfcinfo; - - /* - * We arrange to look up the coercion function only once per series of - * calls, assuming the input data type doesn't change underneath us. - * (Output type can't change.) - */ - my_extra = (atc_extra *) fmgr_info->fn_extra; - if (my_extra == NULL) - { - fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt, - sizeof(atc_extra)); - my_extra = (atc_extra *) fmgr_info->fn_extra; - } - - if (my_extra->srctype != src_elem_type) - { - Oid tgt_type = get_fn_expr_rettype(fmgr_info); - Oid tgt_elem_type; - Oid funcId; - - if (tgt_type == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("could not determine target array type"))); - - tgt_elem_type = get_element_type(tgt_type); - if (tgt_elem_type == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("target type is not an array"))); - - /* - * We don't deal with domain constraints yet, so bail out. This isn't - * currently a problem, because we also don't support arrays of domain - * type elements either. But in the future we might. At that point - * consideration should be given to removing the check below and - * adding a domain constraints check to the coercion. - */ - if (getBaseType(tgt_elem_type) != tgt_elem_type) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("array coercion to domain type elements not " - "currently supported"))); - - if (!find_coercion_pathway(tgt_elem_type, src_elem_type, - COERCION_EXPLICIT, &funcId)) - { - /* should never happen, but check anyway */ - elog(ERROR, "no conversion function from %s to %s", - format_type_be(src_elem_type), - format_type_be(tgt_elem_type)); - } - if (OidIsValid(funcId)) - fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt); - else - my_extra->coerce_finfo.fn_oid = InvalidOid; - my_extra->srctype = src_elem_type; - my_extra->desttype = tgt_elem_type; - } - - /* - * If it's binary-compatible, modify the element type in the array header, - * but otherwise leave the array as we received it. - */ - if (my_extra->coerce_finfo.fn_oid == InvalidOid) - { - ArrayType *result; - - result = (ArrayType *) DatumGetPointer(datumCopy(PointerGetDatum(src), - false, -1)); - ARR_ELEMTYPE(result) = my_extra->desttype; - PG_RETURN_ARRAYTYPE_P(result); - } - - /* - * Use array_map to apply the function to each array element. - * - * We pass on the desttypmod and isExplicit flags whether or not the - * function wants them. - */ - InitFunctionCallInfoData(locfcinfo, &my_extra->coerce_finfo, 3, - NULL, NULL); - locfcinfo.arg[0] = PointerGetDatum(src); - locfcinfo.arg[1] = Int32GetDatum(desttypmod); - locfcinfo.arg[2] = BoolGetDatum(isExplicit); - locfcinfo.argnull[0] = false; - locfcinfo.argnull[1] = false; - locfcinfo.argnull[2] = false; - - return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype, - &my_extra->amstate); -} - -/* - * array_length_coerce -- apply the element type's length-coercion routine - * to each element of the given array. - */ -Datum -array_length_coerce(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 desttypmod = PG_GETARG_INT32(1); - bool isExplicit = PG_GETARG_BOOL(2); - FmgrInfo *fmgr_info = fcinfo->flinfo; - typedef struct - { - Oid elemtype; - FmgrInfo coerce_finfo; - ArrayMapState amstate; - } alc_extra; - alc_extra *my_extra; - FunctionCallInfoData locfcinfo; - - /* If no typmod is provided, shortcircuit the whole thing */ - if (desttypmod < 0) - PG_RETURN_ARRAYTYPE_P(v); - - /* - * We arrange to look up the element type's coercion function only once - * per series of calls, assuming the element type doesn't change - * underneath us. - */ - my_extra = (alc_extra *) fmgr_info->fn_extra; - if (my_extra == NULL) - { - fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt, - sizeof(alc_extra)); - my_extra = (alc_extra *) fmgr_info->fn_extra; - } - - if (my_extra->elemtype != ARR_ELEMTYPE(v)) - { - Oid funcId; - - funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v)); - - if (OidIsValid(funcId)) - fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt); - else - my_extra->coerce_finfo.fn_oid = InvalidOid; - my_extra->elemtype = ARR_ELEMTYPE(v); - } - - /* - * If we didn't find a coercion function, return the array unmodified - * (this should not happen in the normal course of things, but might - * happen if this function is called manually). - */ - if (my_extra->coerce_finfo.fn_oid == InvalidOid) - PG_RETURN_ARRAYTYPE_P(v); - - /* - * Use array_map to apply the function to each array element. - * - * Note: we pass isExplicit whether or not the function wants it ... - */ - InitFunctionCallInfoData(locfcinfo, &my_extra->coerce_finfo, 3, - NULL, NULL); - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(desttypmod); - locfcinfo.arg[2] = BoolGetDatum(isExplicit); - locfcinfo.argnull[0] = false; - locfcinfo.argnull[1] = false; - locfcinfo.argnull[2] = false; - - return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v), - &my_extra->amstate); -} - /* * accumArrayResult - accumulate one (more) Datum for an array result * diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index af363f4acf..b9a026c7ea 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -15,7 +15,7 @@ * * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.93 2007/03/25 19:45:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.94 2007/03/27 23:21:10 tgl Exp $ * * ---------- */ @@ -3871,6 +3871,7 @@ ri_HashCompareOp(Oid eq_opr, Oid typeid) Oid lefttype, righttype, castfunc; + bool arrayCoerce; /* We always need to know how to call the equality operator */ fmgr_info_cxt(get_opcode(eq_opr), &entry->eq_opr_finfo, @@ -3879,13 +3880,19 @@ ri_HashCompareOp(Oid eq_opr, Oid typeid) /* * If we chose to use a cast from FK to PK type, we may have to * apply the cast function to get to the operator's input type. + * + * XXX eventually it would be good to support array-coercion cases + * here and in ri_AttributesEqual(). At the moment there is no + * point because cases involving nonidentical array types will + * be rejected at constraint creation time. */ op_input_types(eq_opr, &lefttype, &righttype); Assert(lefttype == righttype); if (typeid == lefttype) castfunc = InvalidOid; /* simplest case */ else if (!find_coercion_pathway(lefttype, typeid, COERCION_IMPLICIT, - &castfunc)) + &castfunc, &arrayCoerce) + || arrayCoerce) /* XXX fixme */ { /* If target is ANYARRAY, assume it's OK, else punt. */ if (lefttype != ANYARRAYOID) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index da5ab61e84..46cf1dd45a 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.256 2007/03/18 16:50:42 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.257 2007/03/27 23:21:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3123,6 +3123,9 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_RelabelType: return isSimpleNode((Node *) ((RelabelType *) node)->arg, node, prettyFlags); + case T_ArrayCoerceExpr: + return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg, + node, prettyFlags); case T_ConvertRowtypeExpr: return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg, node, prettyFlags); @@ -3588,6 +3591,27 @@ get_rule_expr(Node *node, deparse_context *context, } break; + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; + Node *arg = (Node *) acoerce->arg; + + if (acoerce->coerceformat == COERCE_IMPLICIT_CAST && + !showimplicit) + { + /* don't show the implicit cast */ + get_rule_expr_paren(arg, context, false, node); + } + else + { + get_coercion_expr(arg, context, + acoerce->resulttype, + acoerce->resulttypmod, + node); + } + } + break; + case T_ConvertRowtypeExpr: { ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index a92317aeac..f596220d5a 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.230 2007/03/21 22:18:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.231 2007/03/27 23:21:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1454,54 +1454,26 @@ nulltestsel(PlannerInfo *root, NullTestType nulltesttype, /* * strip_array_coercion - strip binary-compatible relabeling from an array expr * - * For array values, the parser doesn't generate simple RelabelType nodes, - * but function calls of array_type_coerce() or array_type_length_coerce(). - * If we want to cope with binary-compatible situations we have to look - * through these calls whenever the element-type coercion is binary-compatible. + * For array values, the parser normally generates ArrayCoerceExpr conversions, + * but it seems possible that RelabelType might show up. Also, the planner + * is not currently tense about collapsing stacked ArrayCoerceExpr nodes, + * so we need to be ready to deal with more than one level. */ static Node * strip_array_coercion(Node *node) { - /* could be more than one level, so loop */ for (;;) { - if (node && IsA(node, RelabelType)) + if (node && IsA(node, ArrayCoerceExpr) && + ((ArrayCoerceExpr *) node)->elemfuncid == InvalidOid) + { + node = (Node *) ((ArrayCoerceExpr *) node)->arg; + } + else if (node && IsA(node, RelabelType)) { /* We don't really expect this case, but may as well cope */ node = (Node *) ((RelabelType *) node)->arg; } - else if (node && IsA(node, FuncExpr)) - { - FuncExpr *fexpr = (FuncExpr *) node; - Node *arg1; - Oid src_elem_type; - Oid tgt_elem_type; - Oid funcId; - - /* must be the right function(s) */ - if (!(fexpr->funcid == F_ARRAY_TYPE_COERCE || - fexpr->funcid == F_ARRAY_TYPE_LENGTH_COERCE)) - break; - - /* fetch source and destination array element types */ - arg1 = (Node *) linitial(fexpr->args); - src_elem_type = get_element_type(exprType(arg1)); - if (src_elem_type == InvalidOid) - break; /* probably shouldn't happen */ - tgt_elem_type = get_element_type(fexpr->funcresulttype); - if (tgt_elem_type == InvalidOid) - break; /* probably shouldn't happen */ - - /* find out how to coerce */ - if (!find_coercion_pathway(tgt_elem_type, src_elem_type, - COERCION_EXPLICIT, &funcId)) - break; /* definitely shouldn't happen */ - - if (OidIsValid(funcId)) - break; /* non-binary-compatible coercion */ - - node = arg1; /* OK to look through the node */ - } else break; } diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index c36d9fee13..a28acdc2ad 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.104 2007/02/09 03:35:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.105 2007/03/27 23:21:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2061,6 +2061,8 @@ get_call_expr_argtype(Node *expr, int argnum) args = ((DistinctExpr *) expr)->args; else if (IsA(expr, ScalarArrayOpExpr)) args = ((ScalarArrayOpExpr *) expr)->args; + else if (IsA(expr, ArrayCoerceExpr)) + args = list_make1(((ArrayCoerceExpr *) expr)->arg); else if (IsA(expr, NullIfExpr)) args = ((NullIfExpr *) expr)->args; else @@ -2072,12 +2074,16 @@ get_call_expr_argtype(Node *expr, int argnum) argtype = exprType((Node *) list_nth(args, argnum)); /* - * special hack for ScalarArrayOpExpr: what the underlying function will - * actually get passed is the element type of the array. + * special hack for ScalarArrayOpExpr and ArrayCoerceExpr: what the + * underlying function will actually get passed is the element type of + * the array. */ if (IsA(expr, ScalarArrayOpExpr) && argnum == 1) argtype = get_element_type(argtype); + else if (IsA(expr, ArrayCoerceExpr) && + argnum == 0) + argtype = get_element_type(argtype); return argtype; } diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 79dd0cdb10..270687b153 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.395 2007/03/26 16:58:41 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.396 2007/03/27 23:21:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200703261 +#define CATALOG_VERSION_NO 200703271 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 0a096972ed..44e585e9c4 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.450 2007/03/22 20:14:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.451 2007/03/27 23:21:11 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1013,8 +1013,6 @@ DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 1 0 f f f f i 2 2277 "2 DESCR("prepend element onto front of array"); DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 1 0 f f f f i 2 2277 "2277 2277" _null_ _null_ _null_ array_cat - _null_ )); DESCR("concatenate two arrays"); -DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 1 0 f f t f s 1 2277 "2277" _null_ _null_ _null_ array_type_coerce - _null_ )); -DESCR("coerce array to another array type"); DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 1 0 f f t f i 2 1009 "25 25" _null_ _null_ _null_ text_to_array - _null_ )); DESCR("split delimited text into text[]"); DATA(insert OID = 395 ( array_to_string PGNSP PGUID 12 1 0 f f t f i 2 25 "2277 25" _null_ _null_ _null_ array_to_text - _null_ )); @@ -1622,9 +1620,6 @@ DESCR("convert int8 to text"); DATA(insert OID = 1290 ( int8 PGNSP PGUID 12 1 0 f f t f i 1 20 "25" _null_ _null_ _null_ text_int8 - _null_ )); DESCR("convert text to int8"); -DATA(insert OID = 1291 ( array_length_coerce PGNSP PGUID 12 1 0 f f t f s 3 2277 "2277 23 16" _null_ _null_ _null_ array_length_coerce - _null_ )); -DESCR("adjust any array to new element typmod"); - DATA(insert OID = 1292 ( tideq PGNSP PGUID 12 1 0 f f t f i 2 16 "27 27" _null_ _null_ _null_ tideq - _null_ )); DESCR("equal"); DATA(insert OID = 1293 ( currtid PGNSP PGUID 12 1 0 f f t f v 2 27 "26 27" _null_ _null_ _null_ currtid_byreloid - _null_ )); @@ -1786,10 +1781,6 @@ DATA(insert OID = 1370 ( interval PGNSP PGUID 12 1 0 f f t f i 1 1186 "1083" DESCR("convert time to interval"); DATA(insert OID = 1372 ( char_length PGNSP PGUID 12 1 0 f f t f i 1 23 "1042" _null_ _null_ _null_ bpcharlen - _null_ )); DESCR("character length"); - -DATA(insert OID = 1373 ( array_type_length_coerce PGNSP PGUID 12 1 0 f f t f s 3 2277 "2277 23 16" _null_ _null_ _null_ array_type_length_coerce - _null_ )); -DESCR("coerce array to another type and adjust element typmod"); - DATA(insert OID = 1374 ( octet_length PGNSP PGUID 12 1 0 f f t f i 1 23 "25" _null_ _null_ _null_ textoctetlen - _null_ )); DESCR("octet length"); DATA(insert OID = 1375 ( octet_length PGNSP PGUID 12 1 0 f f t f i 1 23 "1042" _null_ _null_ _null_ bpcharoctetlen - _null_ )); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index fe9ce9a4bf..56bac9350f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.170 2007/02/27 01:11:26 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.171 2007/03/27 23:21:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -625,6 +625,20 @@ typedef struct FieldStoreState TupleDesc argdesc; /* tupdesc for most recent input */ } FieldStoreState; +/* ---------------- + * ArrayCoerceExprState node + * ---------------- + */ +typedef struct ArrayCoerceExprState +{ + ExprState xprstate; + ExprState *arg; /* input array value */ + Oid resultelemtype; /* element type of result array */ + FmgrInfo elemfunc; /* lookup info for element coercion function */ + /* use struct pointer to avoid including array.h here */ + struct ArrayMapState *amstate; /* workspace for array_map */ +} ArrayCoerceExprState; + /* ---------------- * ConvertRowtypeExprState node * ---------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 53bd13b5fb..f344e3f5d1 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.196 2007/02/20 17:32:17 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.197 2007/03/27 23:21:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -121,6 +121,7 @@ typedef enum NodeTag T_FieldSelect, T_FieldStore, T_RelabelType, + T_ArrayCoerceExpr, T_ConvertRowtypeExpr, T_CaseExpr, T_CaseWhen, @@ -159,6 +160,7 @@ typedef enum NodeTag T_SubPlanState, T_FieldSelectState, T_FieldStoreState, + T_ArrayCoerceExprState, T_ConvertRowtypeExprState, T_CaseExprState, T_CaseWhenState, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 475bc149d7..ccad1329a6 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.128 2007/03/17 00:11:05 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.129 2007/03/27 23:21:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -550,6 +550,29 @@ typedef struct RelabelType CoercionForm relabelformat; /* how to display this node */ } RelabelType; +/* ---------------- + * ArrayCoerceExpr + * + * ArrayCoerceExpr represents a type coercion from one array type to another, + * which is implemented by applying the indicated element-type coercion + * function to each element of the source array. If elemfuncid is InvalidOid + * then the element types are binary-compatible, but the coercion still + * requires some effort (we have to fix the element type ID stored in the + * array header). + * ---------------- + */ + +typedef struct ArrayCoerceExpr +{ + Expr xpr; + Expr *arg; /* input expression (yields an array) */ + Oid elemfuncid; /* OID of element coercion function, or 0 */ + Oid resulttype; /* output type of coercion (an array type) */ + int32 resulttypmod; /* output typmod (also element typmod) */ + bool isExplicit; /* conversion semantics flag to pass to func */ + CoercionForm coerceformat; /* how to display this node */ +} ArrayCoerceExpr; + /* ---------------- * ConvertRowtypeExpr * diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 40ece05470..23aaa87cfa 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.69 2007/01/05 22:19:57 momjian Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.70 2007/03/27 23:21:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -77,7 +77,8 @@ extern Oid resolve_generic_type(Oid declared_type, extern bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, - Oid *funcid); -extern Oid find_typmod_coercion_function(Oid typeId); + Oid *funcid, bool *arrayCoerce); +extern bool find_typmod_coercion_function(Oid typeId, + Oid *funcid, bool *arrayCoerce); #endif /* PARSE_COERCE_H */ diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 9176a8b288..534adef53f 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -49,7 +49,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.63 2007/02/27 23:48:10 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.64 2007/03/27 23:21:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -198,9 +198,6 @@ extern Datum arraycontained(PG_FUNCTION_ARGS); extern Datum array_dims(PG_FUNCTION_ARGS); extern Datum array_lower(PG_FUNCTION_ARGS); extern Datum array_upper(PG_FUNCTION_ARGS); -extern Datum array_type_coerce(PG_FUNCTION_ARGS); -extern Datum array_type_length_coerce(PG_FUNCTION_ARGS); -extern Datum array_length_coerce(PG_FUNCTION_ARGS); extern Datum array_larger(PG_FUNCTION_ARGS); extern Datum array_smaller(PG_FUNCTION_ARGS); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 165ce0426e..41d2b3e544 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.191 2007/03/25 23:27:59 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.192 2007/03/27 23:21:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4563,6 +4563,9 @@ exec_simple_check_node(Node *node) case T_RelabelType: return exec_simple_check_node((Node *) ((RelabelType *) node)->arg); + case T_ArrayCoerceExpr: + return exec_simple_check_node((Node *) ((ArrayCoerceExpr *) node)->arg); + case T_ConvertRowtypeExpr: return exec_simple_check_node((Node *) ((ConvertRowtypeExpr *) node)->arg);