summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>2010-08-18 12:20:22 +0000
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>2010-08-18 12:20:22 +0000
commitdbc466917383d66ca921224b7bb7b7b0f72e4920 (patch)
tree9274109b1c244ac82422b59b3edc16f36764513d /src
parent277633b7e85167646a4fbd09aa3d0679fef154d6 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/backend/parser/parse_param.c86
1 files changed, 84 insertions, 2 deletions
diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c
index ba91028c891..76a90d77bfc 100644
--- a/src/backend/parser/parse_param.c
+++ b/src/backend/parser/parse_param.c
@@ -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.4.4.1 2010/08/18 12:20:22 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;
}
/*
@@ -171,6 +176,83 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
}
/*
+ * 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.
*/
static Node *