summaryrefslogtreecommitdiffstats
path: root/src/backend/parser/parse_param.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/parser/parse_param.c
parentInitial commit. (diff)
downloadpostgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz
postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/parser/parse_param.c')
-rw-r--r--src/backend/parser/parse_param.c363
1 files changed, 363 insertions, 0 deletions
diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c
new file mode 100644
index 0000000..68a5534
--- /dev/null
+++ b/src/backend/parser/parse_param.c
@@ -0,0 +1,363 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_param.c
+ * handle parameters in parser
+ *
+ * This code covers two cases that are used within the core backend:
+ * * a fixed list of parameters with known types
+ * * an expandable list of parameters whose types can optionally
+ * be determined from context
+ * In both cases, only explicit $n references (ParamRef nodes) are supported.
+ *
+ * Note that other approaches to parameters are possible using the parser
+ * hooks defined in ParseState.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/parser/parse_param.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <limits.h>
+
+#include "catalog/pg_type.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_param.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+
+
+typedef struct FixedParamState
+{
+ Oid *paramTypes; /* array of parameter type OIDs */
+ int numParams; /* number of array entries */
+} FixedParamState;
+
+/*
+ * In the varparams case, the caller-supplied OID array (if any) can be
+ * re-palloc'd larger at need. A zero array entry means that parameter number
+ * hasn't been seen, while UNKNOWNOID means the parameter has been used but
+ * its type is not yet known.
+ */
+typedef struct VarParamState
+{
+ Oid **paramTypes; /* array of parameter type OIDs */
+ int *numParams; /* number of array entries */
+} VarParamState;
+
+static Node *fixed_paramref_hook(ParseState *pstate, ParamRef *pref);
+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 bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
+static bool query_contains_extern_params_walker(Node *node, void *context);
+
+
+/*
+ * Set up to process a query containing references to fixed parameters.
+ */
+void
+parse_fixed_parameters(ParseState *pstate,
+ Oid *paramTypes, int numParams)
+{
+ FixedParamState *parstate = palloc(sizeof(FixedParamState));
+
+ parstate->paramTypes = paramTypes;
+ parstate->numParams = numParams;
+ pstate->p_ref_hook_state = (void *) parstate;
+ pstate->p_paramref_hook = fixed_paramref_hook;
+ /* no need to use p_coerce_param_hook */
+}
+
+/*
+ * Set up to process a query containing references to variable parameters.
+ */
+void
+parse_variable_parameters(ParseState *pstate,
+ Oid **paramTypes, int *numParams)
+{
+ VarParamState *parstate = palloc(sizeof(VarParamState));
+
+ parstate->paramTypes = paramTypes;
+ parstate->numParams = numParams;
+ pstate->p_ref_hook_state = (void *) parstate;
+ pstate->p_paramref_hook = variable_paramref_hook;
+ pstate->p_coerce_param_hook = variable_coerce_param_hook;
+}
+
+/*
+ * Transform a ParamRef using fixed parameter types.
+ */
+static Node *
+fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
+{
+ FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
+ int paramno = pref->number;
+ Param *param;
+
+ /* Check parameter number is valid */
+ if (paramno <= 0 || paramno > parstate->numParams ||
+ !OidIsValid(parstate->paramTypes[paramno - 1]))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_PARAMETER),
+ errmsg("there is no parameter $%d", paramno),
+ parser_errposition(pstate, pref->location)));
+
+ param = makeNode(Param);
+ param->paramkind = PARAM_EXTERN;
+ param->paramid = paramno;
+ param->paramtype = parstate->paramTypes[paramno - 1];
+ param->paramtypmod = -1;
+ param->paramcollid = get_typcollation(param->paramtype);
+ param->location = pref->location;
+
+ return (Node *) param;
+}
+
+/*
+ * Transform a ParamRef using variable parameter types.
+ *
+ * The only difference here is we must enlarge the parameter type array
+ * as needed.
+ */
+static Node *
+variable_paramref_hook(ParseState *pstate, ParamRef *pref)
+{
+ VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
+ int paramno = pref->number;
+ Oid *pptype;
+ Param *param;
+
+ /* Check parameter number is in range */
+ if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_PARAMETER),
+ errmsg("there is no parameter $%d", paramno),
+ parser_errposition(pstate, pref->location)));
+ if (paramno > *parstate->numParams)
+ {
+ /* Need to enlarge param array */
+ if (*parstate->paramTypes)
+ *parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes,
+ paramno * sizeof(Oid));
+ else
+ *parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid));
+ /* Zero out the previously-unreferenced slots */
+ MemSet(*parstate->paramTypes + *parstate->numParams,
+ 0,
+ (paramno - *parstate->numParams) * sizeof(Oid));
+ *parstate->numParams = paramno;
+ }
+
+ /* Locate param's slot in array */
+ pptype = &(*parstate->paramTypes)[paramno - 1];
+
+ /* If not seen before, initialize to UNKNOWN type */
+ if (*pptype == InvalidOid)
+ *pptype = UNKNOWNOID;
+
+ /*
+ * If the argument is of type void and it's procedure call, interpret it
+ * as unknown. This allows the JDBC driver to not have to distinguish
+ * function and procedure calls. See also another component of this hack
+ * in ParseFuncOrColumn().
+ */
+ if (*pptype == VOIDOID && pstate->p_expr_kind == EXPR_KIND_CALL_ARGUMENT)
+ *pptype = UNKNOWNOID;
+
+ param = makeNode(Param);
+ param->paramkind = PARAM_EXTERN;
+ param->paramid = paramno;
+ param->paramtype = *pptype;
+ param->paramtypmod = -1;
+ param->paramcollid = get_typcollation(param->paramtype);
+ param->location = pref->location;
+
+ return (Node *) param;
+}
+
+/*
+ * Coerce a Param to a query-requested datatype, in the varparams case.
+ */
+static Node *
+variable_coerce_param_hook(ParseState *pstate, Param *param,
+ Oid targetTypeId, int32 targetTypeMod,
+ int location)
+{
+ if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
+ {
+ /*
+ * Input is a Param of previously undetermined type, and we want to
+ * update our knowledge of the Param's type.
+ */
+ VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
+ Oid *paramTypes = *parstate->paramTypes;
+ 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 (paramTypes[paramno - 1] == UNKNOWNOID)
+ {
+ /* We've successfully resolved the type */
+ paramTypes[paramno - 1] = targetTypeId;
+ }
+ else if (paramTypes[paramno - 1] == targetTypeId)
+ {
+ /* We previously resolved the type, and it matches */
+ }
+ else
+ {
+ /* Oops */
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+ errmsg("inconsistent types deduced for parameter $%d",
+ paramno),
+ errdetail("%s versus %s",
+ format_type_be(paramTypes[paramno - 1]),
+ format_type_be(targetTypeId)),
+ parser_errposition(pstate, param->location)));
+ }
+
+ param->paramtype = targetTypeId;
+
+ /*
+ * Note: it is tempting here to set the Param's paramtypmod to
+ * targetTypeMod, but that is probably unwise because we have no
+ * infrastructure that enforces that the value delivered for a Param
+ * will match any particular typmod. Leaving it -1 ensures that a
+ * run-time length check/coercion will occur if needed.
+ */
+ param->paramtypmod = -1;
+
+ /*
+ * This module always sets a Param's collation to be the default for
+ * its datatype. If that's not what you want, you should be using the
+ * more general parser substitution hooks.
+ */
+ param->paramcollid = get_typcollation(param->paramtype);
+
+ /* Use the leftmost of the param's and coercion's locations */
+ if (location >= 0 &&
+ (param->location < 0 || location < param->location))
+ param->location = location;
+
+ return (Node *) param;
+ }
+
+ /* Else signal to proceed with normal coercion */
+ return NULL;
+}
+
+/*
+ * Check for consistent assignment of variable parameters after completion
+ * of parsing with parse_variable_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_variable_parameters(ParseState *pstate, Query *query)
+{
+ VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
+
+ /* 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,
+ (void *) pstate, 0);
+}
+
+/*
+ * Traverse a fully-analyzed tree to verify that parameter symbols
+ * match their types. We need this because some Params might still
+ * be UNKNOWN, if there wasn't anything to force their coercion,
+ * and yet other instances seen later might have gotten coerced.
+ */
+static bool
+check_parameter_resolution_walker(Node *node, ParseState *pstate)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Param))
+ {
+ Param *param = (Param *) node;
+
+ if (param->paramkind == PARAM_EXTERN)
+ {
+ VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
+ 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 (param->paramtype != (*parstate->paramTypes)[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 (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ return query_tree_walker((Query *) node,
+ check_parameter_resolution_walker,
+ (void *) pstate, 0);
+ }
+ return expression_tree_walker(node, check_parameter_resolution_walker,
+ (void *) pstate);
+}
+
+/*
+ * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
+ */
+bool
+query_contains_extern_params(Query *query)
+{
+ return query_tree_walker(query,
+ query_contains_extern_params_walker,
+ NULL, 0);
+}
+
+static bool
+query_contains_extern_params_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Param))
+ {
+ Param *param = (Param *) node;
+
+ if (param->paramkind == PARAM_EXTERN)
+ return true;
+ return false;
+ }
+ if (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ return query_tree_walker((Query *) node,
+ query_contains_extern_params_walker,
+ context, 0);
+ }
+ return expression_tree_walker(node, query_contains_extern_params_walker,
+ context);
+}