diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6b99a10..2cb9f31 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -92,6 +92,10 @@ parse_analyze(Node *parseTree, const char *sourceText,
 
 	query = transformStmt(pstate, parseTree);
 
+	/* make sure all is well with parameter types */
+	if (numParams > 0)
+		check_fixed_parameters(pstate, query);
+
 	free_parsestate(pstate);
 
 	return query;
diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c
index 60917f4..7cb34c2 100644
--- a/src/backend/parser/parse_param.c
+++ b/src/backend/parser/parse_param.c
@@ -59,7 +59,10 @@ static Node *variable_coerce_param_hook(ParseState *pstate, Param *param,
 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);
+static bool check_fixed_parameter_resolution_walker(Node *node,
+										ParseState *pstate);
+static bool check_variable_parameter_resolution_walker(Node *node,
+										   ParseState *pstate);
 
 
 /*
@@ -322,6 +325,135 @@ variable_coerce_param_hook(ParseState *pstate, Param *param,
 	return NULL;
 }
 
+
+/*
+ * Check for consistent assignment of unknown parameters after completion
+ * of parsing with parse_fixed_parameters.
+ *
+ * Note: this code intentionally does not check that all parameter positions
+ * were used, nor that all got non-UNKNOWN types assigned.	Caller of parser
+ * should enforce that if it's important.
+ */
+void
+check_fixed_parameters(ParseState *pstate, Query *query)
+{
+	FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
+
+	/*
+	 * If parse_fixed_parameters() didn't resolve any unknown types, there's
+	 * nothing to do.
+	 */
+	if (parstate->unknownParamTypes != NULL)
+		(void) query_tree_walker(query,
+								 check_fixed_parameter_resolution_walker,
+								 (void *) pstate, 0);
+}
+
+/*
+ * Traverse a fully-analyzed tree to verify that all references to unknown
+ * Params are coerced to the same type.  Although we check in
+ * fixed_coerce_param_hook() that an unknown Param is not coerced to different
+ * types at different locations in the query, some Params might still be
+ * uncoerced, if there wasn't anything to force their coercion, and yet other
+ * instances seen later might have gotten coerced.
+ */
+static bool
+check_fixed_parameter_resolution_walker(Node *node, ParseState *pstate)
+{
+	FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
+
+	if (node == NULL)
+		return false;
+
+	/*
+	 * Check if this is a CoerceViaIO(Param of type 'unknown') construct,
+	 * created by parse_fixed_parameters(). In theory, it could be a similar
+	 * construct created by other means, but that doesn't currently happen;
+	 * unknown Params needing coercion are always handled by
+	 * fixed_coerce_param_hook(), so the normal logic in coerce_type() which
+	 * could create CoerceViaIO nodes doesn't get to run on unknown Params.
+	 * This needs to be revisited if CoerceViaIO nodes are created elsewhere
+	 * in the future.
+	 */
+	if (IsA(node, CoerceViaIO))
+	{
+		CoerceViaIO *cvio = (CoerceViaIO *) node;
+
+		if (IsA(cvio->arg, Param))
+		{
+			Param *param = (Param *) node;
+
+			if (param->paramkind == PARAM_EXTERN &&
+				param->paramtype == UNKNOWNOID)
+			{
+				int			paramno = param->paramid;
+
+				/*
+				 * This is an already coerced unknown param. Check that
+				 * the type matches what we recorded in unknownParamTypes.
+				 * It always should, this is just a sanity check. What's
+				 * important is to not recurse into the Param node itself.
+				 */
+
+				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)));
+
+				if (cvio->resulttype != parstate->unknownParamTypes[paramno - 1])
+					ereport(ERROR,
+							(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+					 errmsg("could not determine data type of parameter $%d",
+									paramno),
+							 parser_errposition(pstate, param->location)));
+			}
+			return false;
+		}
+	}
+
+	/*
+	 * If this is a naked unknown Param, not coerced by parse_fixed_param(),
+	 * check that there was no instances of the same Param that was coerced
+	 * to some other type. Such coercions if they exist have set the type in
+	 * unknownParamTypes, so check that it's still invalid.
+	 */
+	if (IsA(node, Param))
+	{
+		Param	   *param = (Param *) node;
+
+		if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
+		{
+			int			paramno = param->paramid;
+
+			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)));
+
+			if (parstate->unknownParamTypes[paramno - 1] != InvalidOid)
+				ereport(ERROR,
+						(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+					 errmsg("could not determine data type of parameter $%d",
+							paramno),
+						 parser_errposition(pstate, param->location)));
+		}
+		return false;
+	}
+	if (IsA(node, Query))
+	{
+		/* Recurse into RTE subquery or not-yet-planned sublink subquery */
+		return query_tree_walker((Query *) node,
+								 check_fixed_parameter_resolution_walker,
+								 (void *) pstate, 0);
+	}
+	return expression_tree_walker(node, check_fixed_parameter_resolution_walker,
+								  (void *) pstate);
+}
+
 /*
  * Check for consistent assignment of variable parameters after completion
  * of parsing with parse_variable_parameters.
@@ -338,7 +470,7 @@ check_variable_parameters(ParseState *pstate, Query *query)
 	/* If numParams is zero then no Params were generated, so no work */
 	if (*parstate->numParams > 0)
 		(void) query_tree_walker(query,
-								 check_parameter_resolution_walker,
+								 check_variable_parameter_resolution_walker,
 								 (void *) pstate, 0);
 }
 
@@ -349,7 +481,7 @@ check_variable_parameters(ParseState *pstate, Query *query)
  * and yet other instances seen later might have gotten coerced.
  */
 static bool
-check_parameter_resolution_walker(Node *node, ParseState *pstate)
+check_variable_parameter_resolution_walker(Node *node, ParseState *pstate)
 {
 	if (node == NULL)
 		return false;
@@ -382,9 +514,10 @@ check_parameter_resolution_walker(Node *node, ParseState *pstate)
 	{
 		/* Recurse into RTE subquery or not-yet-planned sublink subquery */
 		return query_tree_walker((Query *) node,
-								 check_parameter_resolution_walker,
+								 check_variable_parameter_resolution_walker,
 								 (void *) pstate, 0);
 	}
-	return expression_tree_walker(node, check_parameter_resolution_walker,
+	return expression_tree_walker(node,
+								  check_variable_parameter_resolution_walker,
 								  (void *) pstate);
 }
diff --git a/src/include/parser/parse_param.h b/src/include/parser/parse_param.h
index 25037d8..dcd26b9 100644
--- a/src/include/parser/parse_param.h
+++ b/src/include/parser/parse_param.h
@@ -19,6 +19,7 @@ extern void parse_fixed_parameters(ParseState *pstate,
 					   Oid *paramTypes, int numParams);
 extern void parse_variable_parameters(ParseState *pstate,
 						  Oid **paramTypes, int *numParams);
+extern void check_fixed_parameters(ParseState *pstate, Query *query);
 extern void check_variable_parameters(ParseState *pstate, Query *query);
 
 #endif   /* PARSE_PARAM_H */
