Coerce 'unknown' type parameters to the right type in the fixed-params

parse_analyze() function. That case occurs e.g with PL/pgSQL
EXECUTE ... USING 'stringconstant'.

The coercion with a CoerceViaIO node. The result is similar to the coercion
via input function performed for unknown constants in coerce_type(),
except that this happens at runtime.

Backpatch to 9.0. The issue is present in 8.4 as well, but the coerce param
hook infrastructure this patch relies on was introduced in 9.0. Given the
lack of user reports and harmlessness of the bug, it's not worth attempting
a different fix just for 8.4.
This commit is contained in:
Heikki Linnakangas 2010-08-18 12:20:15 +00:00
parent 9a8d15bd41
commit ef71375346

View file

@ -17,7 +17,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_param.c,v 2.4 2010/02/26 02:00:52 momjian Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_param.c,v 2.5 2010/08/18 12:20:15 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -36,6 +36,7 @@ typedef struct FixedParamState
{
Oid *paramTypes; /* array of parameter type OIDs */
int numParams; /* number of array entries */
Oid *unknownParamTypes; /* resolved types of 'unknown' params */
} FixedParamState;
/*
@ -55,6 +56,9 @@ static Node *variable_paramref_hook(ParseState *pstate, ParamRef *pref);
static Node *variable_coerce_param_hook(ParseState *pstate, Param *param,
Oid targetTypeId, int32 targetTypeMod,
int location);
static Node *fixed_coerce_param_hook(ParseState *pstate, Param *param,
Oid targetTypeId, int32 targetTypeMod,
int location);
static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
@ -69,9 +73,10 @@ parse_fixed_parameters(ParseState *pstate,
parstate->paramTypes = paramTypes;
parstate->numParams = numParams;
parstate->unknownParamTypes = NULL;
pstate->p_ref_hook_state = (void *) parstate;
pstate->p_paramref_hook = fixed_paramref_hook;
/* no need to use p_coerce_param_hook */
pstate->p_coerce_param_hook = fixed_coerce_param_hook;
}
/*
@ -170,6 +175,83 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
return (Node *) param;
}
/*
* Coerce a Param to a query-requested datatype, in the fixed params case.
*
* 'unknown' type params are coerced to the type requested, analogous to the
* coercion of unknown constants performed in coerce_type(). We can't change
* the param types like we do in the varparams case, so the coercion is done
* at runtime using CoerceViaIO nodes.
*/
static Node *
fixed_coerce_param_hook(ParseState *pstate, Param *param,
Oid targetTypeId, int32 targetTypeMode,
int location)
{
if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
{
FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
Oid *unknownParamTypes = parstate->unknownParamTypes;
int paramno = param->paramid;
CoerceViaIO *iocoerce;
if (paramno <= 0 || /* shouldn't happen, but... */
paramno > parstate->numParams)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, param->location)));
/* Allocate the array on first use */
if (unknownParamTypes == NULL)
{
unknownParamTypes = palloc0(parstate->numParams * sizeof(Oid));
parstate->unknownParamTypes = unknownParamTypes;
}
/*
* If the same parameter is used multiple times in the query, make
* sure it's always resolved to the same type. The code would cope
* with differing interpretations, but it might lead to surprising
* results. The varparams code forbids that anyway, so better be
* consistent.
*/
if (unknownParamTypes[paramno - 1] == InvalidOid)
{
/* We've successfully resolved the type */
unknownParamTypes[paramno - 1] = targetTypeId;
}
else if (unknownParamTypes[paramno - 1] == targetTypeId)
{
/* We previously resolved the type, and it matches */
}
else
{
/* Ooops */
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
errmsg("inconsistent types deduced for parameter $%d",
paramno),
errdetail("%s versus %s",
format_type_be(unknownParamTypes[paramno - 1]),
format_type_be(targetTypeId)),
parser_errposition(pstate, param->location)));
}
/* Build a CoerceViaIO node */
iocoerce = makeNode(CoerceViaIO);
iocoerce->arg = (Expr *) param;
iocoerce->resulttype = targetTypeId;
iocoerce->coerceformat = COERCE_IMPLICIT_CAST;
iocoerce->location = location;
return (Node *) iocoerce;
}
/* Else signal to proceed with normal coercion */
return NULL;
}
/*
* Coerce a Param to a query-requested datatype, in the varparams case.
*/