check-fixed-unknown-param-consistency.patch
text/x-diff
Filename: check-fixed-unknown-param-consistency.patch
Type: text/x-diff
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/parser/analyze.c | 4 | 0 |
| src/backend/parser/parse_param.c | 138 | 5 |
| src/include/parser/parse_param.h | 1 | 0 |
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 */