Coverage Report

Created: 2026-09-28 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/parser/parse_param.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * parse_param.c
4
 *    handle parameters in parser
5
 *
6
 * This code covers two cases that are used within the core backend:
7
 *    * a fixed list of parameters with known types
8
 *    * an expandable list of parameters whose types can optionally
9
 *      be determined from context
10
 * In both cases, only explicit $n references (ParamRef nodes) are supported.
11
 *
12
 * Note that other approaches to parameters are possible using the parser
13
 * hooks defined in ParseState.
14
 *
15
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
16
 * Portions Copyright (c) 1994, Regents of the University of California
17
 *
18
 *
19
 * IDENTIFICATION
20
 *    src/backend/parser/parse_param.c
21
 *
22
 *-------------------------------------------------------------------------
23
 */
24
25
#include "postgres.h"
26
27
#include <limits.h>
28
29
#include "catalog/pg_type.h"
30
#include "nodes/nodeFuncs.h"
31
#include "parser/parse_param.h"
32
#include "utils/builtins.h"
33
#include "utils/lsyscache.h"
34
#include "utils/memutils.h"
35
36
37
typedef struct FixedParamState
38
{
39
  const Oid  *paramTypes;   /* array of parameter type OIDs */
40
  int     numParams;    /* number of array entries */
41
} FixedParamState;
42
43
/*
44
 * In the varparams case, the caller-supplied OID array (if any) can be
45
 * re-palloc'd larger at need.  A zero array entry means that parameter number
46
 * hasn't been seen, while UNKNOWNOID means the parameter has been used but
47
 * its type is not yet known.
48
 */
49
typedef struct VarParamState
50
{
51
  Oid     **paramTypes;   /* array of parameter type OIDs */
52
  int      *numParams;    /* number of array entries */
53
} VarParamState;
54
55
static Node *fixed_paramref_hook(ParseState *pstate, ParamRef *pref);
56
static Node *variable_paramref_hook(ParseState *pstate, ParamRef *pref);
57
static Node *variable_coerce_param_hook(ParseState *pstate, Param *param,
58
                    Oid targetTypeId, int32 targetTypeMod,
59
                    int location);
60
static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
61
static bool query_contains_extern_params_walker(Node *node, void *context);
62
63
64
/*
65
 * Set up to process a query containing references to fixed parameters.
66
 */
67
void
68
setup_parse_fixed_parameters(ParseState *pstate,
69
               const Oid *paramTypes, int numParams)
70
0
{
71
0
  FixedParamState *parstate = palloc_object(FixedParamState);
72
73
0
  parstate->paramTypes = paramTypes;
74
0
  parstate->numParams = numParams;
75
0
  pstate->p_ref_hook_state = parstate;
76
0
  pstate->p_paramref_hook = fixed_paramref_hook;
77
  /* no need to use p_coerce_param_hook */
78
0
}
79
80
/*
81
 * Set up to process a query containing references to variable parameters.
82
 */
83
void
84
setup_parse_variable_parameters(ParseState *pstate,
85
                Oid **paramTypes, int *numParams)
86
0
{
87
0
  VarParamState *parstate = palloc_object(VarParamState);
88
89
0
  parstate->paramTypes = paramTypes;
90
0
  parstate->numParams = numParams;
91
0
  pstate->p_ref_hook_state = parstate;
92
0
  pstate->p_paramref_hook = variable_paramref_hook;
93
0
  pstate->p_coerce_param_hook = variable_coerce_param_hook;
94
0
}
95
96
/*
97
 * Transform a ParamRef using fixed parameter types.
98
 */
99
static Node *
100
fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
101
0
{
102
0
  FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
103
0
  int     paramno = pref->number;
104
0
  Param    *param;
105
106
  /* Check parameter number is valid */
107
0
  if (paramno <= 0 || paramno > parstate->numParams ||
108
0
    !OidIsValid(parstate->paramTypes[paramno - 1]))
109
0
    ereport(ERROR,
110
0
        (errcode(ERRCODE_UNDEFINED_PARAMETER),
111
0
         errmsg("there is no parameter $%d", paramno),
112
0
         parser_errposition(pstate, pref->location)));
113
114
0
  param = makeNode(Param);
115
0
  param->paramkind = PARAM_EXTERN;
116
0
  param->paramid = paramno;
117
0
  param->paramtype = parstate->paramTypes[paramno - 1];
118
0
  param->paramtypmod = -1;
119
0
  param->paramcollid = get_typcollation(param->paramtype);
120
0
  param->location = pref->location;
121
122
0
  return (Node *) param;
123
0
}
124
125
/*
126
 * Transform a ParamRef using variable parameter types.
127
 *
128
 * The only difference here is we must enlarge the parameter type array
129
 * as needed.
130
 */
131
static Node *
132
variable_paramref_hook(ParseState *pstate, ParamRef *pref)
133
0
{
134
0
  VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
135
0
  int     paramno = pref->number;
136
0
  Oid      *pptype;
137
0
  Param    *param;
138
139
  /* Check parameter number is in range */
140
0
  if (paramno <= 0 || paramno > MaxAllocSize / sizeof(Oid))
141
0
    ereport(ERROR,
142
0
        (errcode(ERRCODE_UNDEFINED_PARAMETER),
143
0
         errmsg("there is no parameter $%d", paramno),
144
0
         parser_errposition(pstate, pref->location)));
145
0
  if (paramno > *parstate->numParams)
146
0
  {
147
    /* Need to enlarge param array */
148
0
    if (*parstate->paramTypes)
149
0
      *parstate->paramTypes = repalloc0_array(*parstate->paramTypes, Oid,
150
0
                          *parstate->numParams, paramno);
151
0
    else
152
0
      *parstate->paramTypes = palloc0_array(Oid, paramno);
153
0
    *parstate->numParams = paramno;
154
0
  }
155
156
  /* Locate param's slot in array */
157
0
  pptype = &(*parstate->paramTypes)[paramno - 1];
158
159
  /* If not seen before, initialize to UNKNOWN type */
160
0
  if (*pptype == InvalidOid)
161
0
    *pptype = UNKNOWNOID;
162
163
  /*
164
   * If the argument is of type void and it's procedure call, interpret it
165
   * as unknown.  This allows the JDBC driver to not have to distinguish
166
   * function and procedure calls.  See also another component of this hack
167
   * in ParseFuncOrColumn().
168
   */
169
0
  if (*pptype == VOIDOID && pstate->p_expr_kind == EXPR_KIND_CALL_ARGUMENT)
170
0
    *pptype = UNKNOWNOID;
171
172
0
  param = makeNode(Param);
173
0
  param->paramkind = PARAM_EXTERN;
174
0
  param->paramid = paramno;
175
0
  param->paramtype = *pptype;
176
0
  param->paramtypmod = -1;
177
0
  param->paramcollid = get_typcollation(param->paramtype);
178
0
  param->location = pref->location;
179
180
0
  return (Node *) param;
181
0
}
182
183
/*
184
 * Coerce a Param to a query-requested datatype, in the varparams case.
185
 */
186
static Node *
187
variable_coerce_param_hook(ParseState *pstate, Param *param,
188
               Oid targetTypeId, int32 targetTypeMod,
189
               int location)
190
0
{
191
0
  if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
192
0
  {
193
    /*
194
     * Input is a Param of previously undetermined type, and we want to
195
     * update our knowledge of the Param's type.
196
     */
197
0
    VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
198
0
    Oid      *paramTypes = *parstate->paramTypes;
199
0
    int     paramno = param->paramid;
200
201
0
    if (paramno <= 0 ||   /* shouldn't happen, but... */
202
0
      paramno > *parstate->numParams)
203
0
      ereport(ERROR,
204
0
          (errcode(ERRCODE_UNDEFINED_PARAMETER),
205
0
           errmsg("there is no parameter $%d", paramno),
206
0
           parser_errposition(pstate, param->location)));
207
208
0
    if (paramTypes[paramno - 1] == UNKNOWNOID)
209
0
    {
210
      /* We've successfully resolved the type */
211
0
      paramTypes[paramno - 1] = targetTypeId;
212
0
    }
213
0
    else if (paramTypes[paramno - 1] == targetTypeId)
214
0
    {
215
      /* We previously resolved the type, and it matches */
216
0
    }
217
0
    else
218
0
    {
219
      /* Oops */
220
0
      ereport(ERROR,
221
0
          (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
222
0
           errmsg("inconsistent types deduced for parameter $%d",
223
0
              paramno),
224
0
           errdetail("%s versus %s",
225
0
                 format_type_be(paramTypes[paramno - 1]),
226
0
                 format_type_be(targetTypeId)),
227
0
           parser_errposition(pstate, param->location)));
228
0
    }
229
230
0
    param->paramtype = targetTypeId;
231
232
    /*
233
     * Note: it is tempting here to set the Param's paramtypmod to
234
     * targetTypeMod, but that is probably unwise because we have no
235
     * infrastructure that enforces that the value delivered for a Param
236
     * will match any particular typmod.  Leaving it -1 ensures that a
237
     * run-time length check/coercion will occur if needed.
238
     */
239
0
    param->paramtypmod = -1;
240
241
    /*
242
     * This module always sets a Param's collation to be the default for
243
     * its datatype.  If that's not what you want, you should be using the
244
     * more general parser substitution hooks.
245
     */
246
0
    param->paramcollid = get_typcollation(param->paramtype);
247
248
    /* Use the leftmost of the param's and coercion's locations */
249
0
    if (location >= 0 &&
250
0
      (param->location < 0 || location < param->location))
251
0
      param->location = location;
252
253
0
    return (Node *) param;
254
0
  }
255
256
  /* Else signal to proceed with normal coercion */
257
0
  return NULL;
258
0
}
259
260
/*
261
 * Check for consistent assignment of variable parameters after completion
262
 * of parsing with parse_variable_parameters.
263
 *
264
 * Note: this code intentionally does not check that all parameter positions
265
 * were used, nor that all got non-UNKNOWN types assigned.  Caller of parser
266
 * should enforce that if it's important.
267
 */
268
void
269
check_variable_parameters(ParseState *pstate, Query *query)
270
0
{
271
0
  VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
272
273
  /* If numParams is zero then no Params were generated, so no work */
274
0
  if (*parstate->numParams > 0)
275
0
    (void) query_tree_walker(query,
276
0
                 check_parameter_resolution_walker,
277
0
                 pstate, 0);
278
0
}
279
280
/*
281
 * Traverse a fully-analyzed tree to verify that parameter symbols
282
 * match their types.  We need this because some Params might still
283
 * be UNKNOWN, if there wasn't anything to force their coercion,
284
 * and yet other instances seen later might have gotten coerced.
285
 */
286
static bool
287
check_parameter_resolution_walker(Node *node, ParseState *pstate)
288
0
{
289
0
  if (node == NULL)
290
0
    return false;
291
0
  if (IsA(node, Param))
292
0
  {
293
0
    Param    *param = (Param *) node;
294
295
0
    if (param->paramkind == PARAM_EXTERN)
296
0
    {
297
0
      VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
298
0
      int     paramno = param->paramid;
299
300
0
      if (paramno <= 0 || /* shouldn't happen, but... */
301
0
        paramno > *parstate->numParams)
302
0
        ereport(ERROR,
303
0
            (errcode(ERRCODE_UNDEFINED_PARAMETER),
304
0
             errmsg("there is no parameter $%d", paramno),
305
0
             parser_errposition(pstate, param->location)));
306
307
0
      if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
308
0
        ereport(ERROR,
309
0
            (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
310
0
             errmsg("could not determine data type of parameter $%d",
311
0
                paramno),
312
0
             parser_errposition(pstate, param->location)));
313
0
    }
314
0
    return false;
315
0
  }
316
0
  if (IsA(node, Query))
317
0
  {
318
    /* Recurse into RTE subquery or not-yet-planned sublink subquery */
319
0
    return query_tree_walker((Query *) node,
320
0
                 check_parameter_resolution_walker,
321
0
                 pstate, 0);
322
0
  }
323
0
  return expression_tree_walker(node, check_parameter_resolution_walker,
324
0
                  pstate);
325
0
}
326
327
/*
328
 * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
329
 */
330
bool
331
query_contains_extern_params(Query *query)
332
0
{
333
0
  return query_tree_walker(query,
334
0
               query_contains_extern_params_walker,
335
0
               NULL, 0);
336
0
}
337
338
static bool
339
query_contains_extern_params_walker(Node *node, void *context)
340
0
{
341
0
  if (node == NULL)
342
0
    return false;
343
0
  if (IsA(node, Param))
344
0
  {
345
0
    Param    *param = (Param *) node;
346
347
0
    if (param->paramkind == PARAM_EXTERN)
348
0
      return true;
349
0
    return false;
350
0
  }
351
0
  if (IsA(node, Query))
352
0
  {
353
    /* Recurse into RTE subquery or not-yet-planned sublink subquery */
354
0
    return query_tree_walker((Query *) node,
355
0
                 query_contains_extern_params_walker,
356
0
                 context, 0);
357
0
  }
358
0
  return expression_tree_walker(node, query_contains_extern_params_walker,
359
0
                  context);
360
0
}