Fix array coercion expressions to ensure that the correct volatility is

seen by code inspecting the expression.  The best way to do this seems
to be to drop the original representation as a function invocation, and
instead make a special expression node type that represents applying
the element-type coercion function to each array element.  In this way
the element function is exposed and will be checked for volatility.
Per report from Guillaume Smet.
This commit is contained in:
Tom Lane 2007-03-27 23:21:12 +00:00
parent 87564ffc6a
commit bf94076348
24 changed files with 564 additions and 440 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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))

View File

@ -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 */

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}
/*

View File

@ -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;

View File

@ -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
*

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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_ ));

View File

@ -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
* ----------------

View File

@ -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,

View File

@ -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
*

View File

@ -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 */

View File

@ -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);

View File

@ -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);