Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit bf94076

Browse files
committed
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.
1 parent 87564ff commit bf94076

24 files changed

+565
-441
lines changed

src/backend/catalog/dependency.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.64 2007/02/14 01:58:56 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.65 2007/03/27 23:21:08 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1315,6 +1315,17 @@ find_expr_references_walker(Node *node,
13151315
add_object_address(OCLASS_TYPE, relab->resulttype, 0,
13161316
context->addrs);
13171317
}
1318+
if (IsA(node, ArrayCoerceExpr))
1319+
{
1320+
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
1321+
1322+
if (OidIsValid(acoerce->elemfuncid))
1323+
add_object_address(OCLASS_PROC, acoerce->elemfuncid, 0,
1324+
context->addrs);
1325+
add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
1326+
context->addrs);
1327+
/* fall through to examine arguments */
1328+
}
13181329
if (IsA(node, ConvertRowtypeExpr))
13191330
{
13201331
ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;

src/backend/executor/execQual.c

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.215 2007/02/27 23:48:07 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.216 2007/03/27 23:21:08 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -145,6 +145,9 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate,
145145
static Datum ExecEvalRelabelType(GenericExprState *exprstate,
146146
ExprContext *econtext,
147147
bool *isNull, ExprDoneCond *isDone);
148+
static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
149+
ExprContext *econtext,
150+
bool *isNull, ExprDoneCond *isDone);
148151

149152

150153
/* ----------------------------------------------------------------
@@ -3501,6 +3504,83 @@ ExecEvalRelabelType(GenericExprState *exprstate,
35013504
return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
35023505
}
35033506

3507+
/* ----------------------------------------------------------------
3508+
* ExecEvalArrayCoerceExpr
3509+
*
3510+
* Evaluate an ArrayCoerceExpr node.
3511+
* ----------------------------------------------------------------
3512+
*/
3513+
static Datum
3514+
ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
3515+
ExprContext *econtext,
3516+
bool *isNull, ExprDoneCond *isDone)
3517+
{
3518+
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr;
3519+
Datum result;
3520+
ArrayType *array;
3521+
FunctionCallInfoData locfcinfo;
3522+
3523+
result = ExecEvalExpr(astate->arg, econtext, isNull, isDone);
3524+
3525+
if (isDone && *isDone == ExprEndResult)
3526+
return result; /* nothing to do */
3527+
if (*isNull)
3528+
return result; /* nothing to do */
3529+
3530+
/*
3531+
* If it's binary-compatible, modify the element type in the array header,
3532+
* but otherwise leave the array as we received it.
3533+
*/
3534+
if (!OidIsValid(acoerce->elemfuncid))
3535+
{
3536+
/* Detoast input array if necessary, and copy in any case */
3537+
array = DatumGetArrayTypePCopy(result);
3538+
ARR_ELEMTYPE(array) = astate->resultelemtype;
3539+
PG_RETURN_ARRAYTYPE_P(array);
3540+
}
3541+
3542+
/* Detoast input array if necessary, but don't make a useless copy */
3543+
array = DatumGetArrayTypeP(result);
3544+
3545+
/* Initialize function cache if first time through */
3546+
if (astate->elemfunc.fn_oid == InvalidOid)
3547+
{
3548+
AclResult aclresult;
3549+
3550+
/* Check permission to call function */
3551+
aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
3552+
ACL_EXECUTE);
3553+
if (aclresult != ACLCHECK_OK)
3554+
aclcheck_error(aclresult, ACL_KIND_PROC,
3555+
get_func_name(acoerce->elemfuncid));
3556+
3557+
/* Set up the primary fmgr lookup information */
3558+
fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
3559+
econtext->ecxt_per_query_memory);
3560+
3561+
/* Initialize additional info */
3562+
astate->elemfunc.fn_expr = (Node *) acoerce;
3563+
}
3564+
3565+
/*
3566+
* Use array_map to apply the function to each array element.
3567+
*
3568+
* We pass on the desttypmod and isExplicit flags whether or not the
3569+
* function wants them.
3570+
*/
3571+
InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3,
3572+
NULL, NULL);
3573+
locfcinfo.arg[0] = PointerGetDatum(array);
3574+
locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
3575+
locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
3576+
locfcinfo.argnull[0] = false;
3577+
locfcinfo.argnull[1] = false;
3578+
locfcinfo.argnull[2] = false;
3579+
3580+
return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype,
3581+
astate->amstate);
3582+
}
3583+
35043584

35053585
/*
35063586
* ExecEvalExprSwitchContext
@@ -3770,6 +3850,26 @@ ExecInitExpr(Expr *node, PlanState *parent)
37703850
state = (ExprState *) gstate;
37713851
}
37723852
break;
3853+
case T_ArrayCoerceExpr:
3854+
{
3855+
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
3856+
ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
3857+
3858+
astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
3859+
astate->arg = ExecInitExpr(acoerce->arg, parent);
3860+
astate->resultelemtype = get_element_type(acoerce->resulttype);
3861+
if (astate->resultelemtype == InvalidOid)
3862+
ereport(ERROR,
3863+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3864+
errmsg("target type is not an array")));
3865+
/* Arrays over domains aren't supported yet */
3866+
Assert(getBaseType(astate->resultelemtype) ==
3867+
astate->resultelemtype);
3868+
astate->elemfunc.fn_oid = InvalidOid; /* not initialized */
3869+
astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
3870+
state = (ExprState *) astate;
3871+
}
3872+
break;
37733873
case T_ConvertRowtypeExpr:
37743874
{
37753875
ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;

src/backend/nodes/copyfuncs.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Portions Copyright (c) 1994, Regents of the University of California
1616
*
1717
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.371 2007/03/17 00:11:03 tgl Exp $
18+
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.372 2007/03/27 23:21:09 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -1020,6 +1020,24 @@ _copyRelabelType(RelabelType *from)
10201020
return newnode;
10211021
}
10221022

1023+
/*
1024+
* _copyArrayCoerceExpr
1025+
*/
1026+
static ArrayCoerceExpr *
1027+
_copyArrayCoerceExpr(ArrayCoerceExpr *from)
1028+
{
1029+
ArrayCoerceExpr *newnode = makeNode(ArrayCoerceExpr);
1030+
1031+
COPY_NODE_FIELD(arg);
1032+
COPY_SCALAR_FIELD(elemfuncid);
1033+
COPY_SCALAR_FIELD(resulttype);
1034+
COPY_SCALAR_FIELD(resulttypmod);
1035+
COPY_SCALAR_FIELD(isExplicit);
1036+
COPY_SCALAR_FIELD(coerceformat);
1037+
1038+
return newnode;
1039+
}
1040+
10231041
/*
10241042
* _copyConvertRowtypeExpr
10251043
*/
@@ -3067,6 +3085,9 @@ copyObject(void *from)
30673085
case T_RelabelType:
30683086
retval = _copyRelabelType(from);
30693087
break;
3088+
case T_ArrayCoerceExpr:
3089+
retval = _copyArrayCoerceExpr(from);
3090+
break;
30703091
case T_ConvertRowtypeExpr:
30713092
retval = _copyConvertRowtypeExpr(from);
30723093
break;

src/backend/nodes/equalfuncs.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* Portions Copyright (c) 1994, Regents of the University of California
1919
*
2020
* IDENTIFICATION
21-
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.302 2007/03/17 00:11:03 tgl Exp $
21+
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.303 2007/03/27 23:21:09 tgl Exp $
2222
*
2323
*-------------------------------------------------------------------------
2424
*/
@@ -359,6 +359,27 @@ _equalRelabelType(RelabelType *a, RelabelType *b)
359359
return true;
360360
}
361361

362+
static bool
363+
_equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b)
364+
{
365+
COMPARE_NODE_FIELD(arg);
366+
COMPARE_SCALAR_FIELD(elemfuncid);
367+
COMPARE_SCALAR_FIELD(resulttype);
368+
COMPARE_SCALAR_FIELD(resulttypmod);
369+
COMPARE_SCALAR_FIELD(isExplicit);
370+
371+
/*
372+
* Special-case COERCE_DONTCARE, so that planner can build coercion nodes
373+
* that are equal() to both explicit and implicit coercions.
374+
*/
375+
if (a->coerceformat != b->coerceformat &&
376+
a->coerceformat != COERCE_DONTCARE &&
377+
b->coerceformat != COERCE_DONTCARE)
378+
return false;
379+
380+
return true;
381+
}
382+
362383
static bool
363384
_equalConvertRowtypeExpr(ConvertRowtypeExpr *a, ConvertRowtypeExpr *b)
364385
{
@@ -2013,6 +2034,9 @@ equal(void *a, void *b)
20132034
case T_RelabelType:
20142035
retval = _equalRelabelType(a, b);
20152036
break;
2037+
case T_ArrayCoerceExpr:
2038+
retval = _equalArrayCoerceExpr(a, b);
2039+
break;
20162040
case T_ConvertRowtypeExpr:
20172041
retval = _equalConvertRowtypeExpr(a, b);
20182042
break;

src/backend/nodes/outfuncs.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.304 2007/03/17 00:11:03 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.305 2007/03/27 23:21:09 tgl Exp $
1212
*
1313
* NOTES
1414
* Every node type that can appear in stored rules' parsetrees *must*
@@ -869,6 +869,19 @@ _outRelabelType(StringInfo str, RelabelType *node)
869869
WRITE_ENUM_FIELD(relabelformat, CoercionForm);
870870
}
871871

872+
static void
873+
_outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node)
874+
{
875+
WRITE_NODE_TYPE("ARRAYCOERCEEXPR");
876+
877+
WRITE_NODE_FIELD(arg);
878+
WRITE_OID_FIELD(elemfuncid);
879+
WRITE_OID_FIELD(resulttype);
880+
WRITE_INT_FIELD(resulttypmod);
881+
WRITE_BOOL_FIELD(isExplicit);
882+
WRITE_ENUM_FIELD(coerceformat, CoercionForm);
883+
}
884+
872885
static void
873886
_outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node)
874887
{
@@ -2149,6 +2162,9 @@ _outNode(StringInfo str, void *obj)
21492162
case T_RelabelType:
21502163
_outRelabelType(str, obj);
21512164
break;
2165+
case T_ArrayCoerceExpr:
2166+
_outArrayCoerceExpr(str, obj);
2167+
break;
21522168
case T_ConvertRowtypeExpr:
21532169
_outConvertRowtypeExpr(str, obj);
21542170
break;

src/backend/nodes/readfuncs.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.204 2007/03/17 00:11:04 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.205 2007/03/27 23:21:09 tgl Exp $
1212
*
1313
* NOTES
1414
* Path and Plan nodes do not have any readfuncs support, because we
@@ -584,6 +584,24 @@ _readRelabelType(void)
584584
READ_DONE();
585585
}
586586

587+
/*
588+
* _readArrayCoerceExpr
589+
*/
590+
static ArrayCoerceExpr *
591+
_readArrayCoerceExpr(void)
592+
{
593+
READ_LOCALS(ArrayCoerceExpr);
594+
595+
READ_NODE_FIELD(arg);
596+
READ_OID_FIELD(elemfuncid);
597+
READ_OID_FIELD(resulttype);
598+
READ_INT_FIELD(resulttypmod);
599+
READ_BOOL_FIELD(isExplicit);
600+
READ_ENUM_FIELD(coerceformat, CoercionForm);
601+
602+
READ_DONE();
603+
}
604+
587605
/*
588606
* _readConvertRowtypeExpr
589607
*/
@@ -1024,6 +1042,8 @@ parseNodeString(void)
10241042
return_value = _readFieldStore();
10251043
else if (MATCH("RELABELTYPE", 11))
10261044
return_value = _readRelabelType();
1045+
else if (MATCH("ARRAYCOERCEEXPR", 15))
1046+
return_value = _readArrayCoerceExpr();
10271047
else if (MATCH("CONVERTROWTYPEEXPR", 18))
10281048
return_value = _readConvertRowtypeExpr();
10291049
else if (MATCH("CASE", 4))

src/backend/optimizer/path/costsize.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
* Portions Copyright (c) 1994, Regents of the University of California
5555
*
5656
* IDENTIFICATION
57-
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.178 2007/02/22 22:00:24 tgl Exp $
57+
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.179 2007/03/27 23:21:09 tgl Exp $
5858
*
5959
*-------------------------------------------------------------------------
6060
*/
@@ -1891,6 +1891,15 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
18911891
context->total.per_tuple += get_func_cost(saop->opfuncid) *
18921892
cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
18931893
}
1894+
else if (IsA(node, ArrayCoerceExpr))
1895+
{
1896+
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
1897+
Node *arraynode = (Node *) acoerce->arg;
1898+
1899+
if (OidIsValid(acoerce->elemfuncid))
1900+
context->total.per_tuple += get_func_cost(acoerce->elemfuncid) *
1901+
cpu_operator_cost * estimate_array_length(arraynode);
1902+
}
18941903
else if (IsA(node, RowCompareExpr))
18951904
{
18961905
/* Conservatively assume we will check all the columns */

0 commit comments

Comments
 (0)