Coverage Report

Created: 2025-07-03 06:49

/src/postgres/src/backend/nodes/params.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * params.c
4
 *    Support for finding the values associated with Param nodes.
5
 *
6
 *
7
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8
 * Portions Copyright (c) 1994, Regents of the University of California
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/nodes/params.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
16
#include "postgres.h"
17
18
#include "access/xact.h"
19
#include "fmgr.h"
20
#include "mb/stringinfo_mb.h"
21
#include "nodes/params.h"
22
#include "parser/parse_node.h"
23
#include "storage/shmem.h"
24
#include "utils/datum.h"
25
#include "utils/lsyscache.h"
26
#include "utils/memutils.h"
27
28
29
static void paramlist_parser_setup(ParseState *pstate, void *arg);
30
static Node *paramlist_param_ref(ParseState *pstate, ParamRef *pref);
31
32
33
/*
34
 * Allocate and initialize a new ParamListInfo structure.
35
 *
36
 * To make a new structure for the "dynamic" way (with hooks), pass 0 for
37
 * numParams and set numParams manually.
38
 *
39
 * A default parserSetup function is supplied automatically.  Callers may
40
 * override it if they choose.  (Note that most use-cases for ParamListInfos
41
 * will never use the parserSetup function anyway.)
42
 */
43
ParamListInfo
44
makeParamList(int numParams)
45
0
{
46
0
  ParamListInfo retval;
47
0
  Size    size;
48
49
0
  size = offsetof(ParamListInfoData, params) +
50
0
    numParams * sizeof(ParamExternData);
51
52
0
  retval = (ParamListInfo) palloc(size);
53
0
  retval->paramFetch = NULL;
54
0
  retval->paramFetchArg = NULL;
55
0
  retval->paramCompile = NULL;
56
0
  retval->paramCompileArg = NULL;
57
0
  retval->parserSetup = paramlist_parser_setup;
58
0
  retval->parserSetupArg = retval;
59
0
  retval->paramValuesStr = NULL;
60
0
  retval->numParams = numParams;
61
62
0
  return retval;
63
0
}
64
65
/*
66
 * Copy a ParamListInfo structure.
67
 *
68
 * The result is allocated in CurrentMemoryContext.
69
 *
70
 * Note: the intent of this function is to make a static, self-contained
71
 * set of parameter values.  If dynamic parameter hooks are present, we
72
 * intentionally do not copy them into the result.  Rather, we forcibly
73
 * instantiate all available parameter values and copy the datum values.
74
 *
75
 * paramValuesStr is not copied, either.
76
 */
77
ParamListInfo
78
copyParamList(ParamListInfo from)
79
0
{
80
0
  ParamListInfo retval;
81
82
0
  if (from == NULL || from->numParams <= 0)
83
0
    return NULL;
84
85
0
  retval = makeParamList(from->numParams);
86
87
0
  for (int i = 0; i < from->numParams; i++)
88
0
  {
89
0
    ParamExternData *oprm;
90
0
    ParamExternData *nprm = &retval->params[i];
91
0
    ParamExternData prmdata;
92
0
    int16   typLen;
93
0
    bool    typByVal;
94
95
    /* give hook a chance in case parameter is dynamic */
96
0
    if (from->paramFetch != NULL)
97
0
      oprm = from->paramFetch(from, i + 1, false, &prmdata);
98
0
    else
99
0
      oprm = &from->params[i];
100
101
    /* flat-copy the parameter info */
102
0
    *nprm = *oprm;
103
104
    /* need datumCopy in case it's a pass-by-reference datatype */
105
0
    if (nprm->isnull || !OidIsValid(nprm->ptype))
106
0
      continue;
107
0
    get_typlenbyval(nprm->ptype, &typLen, &typByVal);
108
0
    nprm->value = datumCopy(nprm->value, typByVal, typLen);
109
0
  }
110
111
0
  return retval;
112
0
}
113
114
115
/*
116
 * Set up to parse a query containing references to parameters
117
 * sourced from a ParamListInfo.
118
 */
119
static void
120
paramlist_parser_setup(ParseState *pstate, void *arg)
121
0
{
122
0
  pstate->p_paramref_hook = paramlist_param_ref;
123
  /* no need to use p_coerce_param_hook */
124
0
  pstate->p_ref_hook_state = arg;
125
0
}
126
127
/*
128
 * Transform a ParamRef using parameter type data from a ParamListInfo.
129
 */
130
static Node *
131
paramlist_param_ref(ParseState *pstate, ParamRef *pref)
132
0
{
133
0
  ParamListInfo paramLI = (ParamListInfo) pstate->p_ref_hook_state;
134
0
  int     paramno = pref->number;
135
0
  ParamExternData *prm;
136
0
  ParamExternData prmdata;
137
0
  Param    *param;
138
139
  /* check parameter number is valid */
140
0
  if (paramno <= 0 || paramno > paramLI->numParams)
141
0
    return NULL;
142
143
  /* give hook a chance in case parameter is dynamic */
144
0
  if (paramLI->paramFetch != NULL)
145
0
    prm = paramLI->paramFetch(paramLI, paramno, false, &prmdata);
146
0
  else
147
0
    prm = &paramLI->params[paramno - 1];
148
149
0
  if (!OidIsValid(prm->ptype))
150
0
    return NULL;
151
152
0
  param = makeNode(Param);
153
0
  param->paramkind = PARAM_EXTERN;
154
0
  param->paramid = paramno;
155
0
  param->paramtype = prm->ptype;
156
0
  param->paramtypmod = -1;
157
0
  param->paramcollid = get_typcollation(param->paramtype);
158
0
  param->location = pref->location;
159
160
0
  return (Node *) param;
161
0
}
162
163
/*
164
 * Estimate the amount of space required to serialize a ParamListInfo.
165
 */
166
Size
167
EstimateParamListSpace(ParamListInfo paramLI)
168
0
{
169
0
  int     i;
170
0
  Size    sz = sizeof(int);
171
172
0
  if (paramLI == NULL || paramLI->numParams <= 0)
173
0
    return sz;
174
175
0
  for (i = 0; i < paramLI->numParams; i++)
176
0
  {
177
0
    ParamExternData *prm;
178
0
    ParamExternData prmdata;
179
0
    Oid     typeOid;
180
0
    int16   typLen;
181
0
    bool    typByVal;
182
183
    /* give hook a chance in case parameter is dynamic */
184
0
    if (paramLI->paramFetch != NULL)
185
0
      prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
186
0
    else
187
0
      prm = &paramLI->params[i];
188
189
0
    typeOid = prm->ptype;
190
191
0
    sz = add_size(sz, sizeof(Oid)); /* space for type OID */
192
0
    sz = add_size(sz, sizeof(uint16));  /* space for pflags */
193
194
    /* space for datum/isnull */
195
0
    if (OidIsValid(typeOid))
196
0
      get_typlenbyval(typeOid, &typLen, &typByVal);
197
0
    else
198
0
    {
199
      /* If no type OID, assume by-value, like copyParamList does. */
200
0
      typLen = sizeof(Datum);
201
0
      typByVal = true;
202
0
    }
203
0
    sz = add_size(sz,
204
0
            datumEstimateSpace(prm->value, prm->isnull, typByVal, typLen));
205
0
  }
206
207
0
  return sz;
208
0
}
209
210
/*
211
 * Serialize a ParamListInfo structure into caller-provided storage.
212
 *
213
 * We write the number of parameters first, as a 4-byte integer, and then
214
 * write details for each parameter in turn.  The details for each parameter
215
 * consist of a 4-byte type OID, 2 bytes of flags, and then the datum as
216
 * serialized by datumSerialize().  The caller is responsible for ensuring
217
 * that there is enough storage to store the number of bytes that will be
218
 * written; use EstimateParamListSpace to find out how many will be needed.
219
 * *start_address is updated to point to the byte immediately following those
220
 * written.
221
 *
222
 * RestoreParamList can be used to recreate a ParamListInfo based on the
223
 * serialized representation; this will be a static, self-contained copy
224
 * just as copyParamList would create.
225
 *
226
 * paramValuesStr is not included.
227
 */
228
void
229
SerializeParamList(ParamListInfo paramLI, char **start_address)
230
0
{
231
0
  int     nparams;
232
0
  int     i;
233
234
  /* Write number of parameters. */
235
0
  if (paramLI == NULL || paramLI->numParams <= 0)
236
0
    nparams = 0;
237
0
  else
238
0
    nparams = paramLI->numParams;
239
0
  memcpy(*start_address, &nparams, sizeof(int));
240
0
  *start_address += sizeof(int);
241
242
  /* Write each parameter in turn. */
243
0
  for (i = 0; i < nparams; i++)
244
0
  {
245
0
    ParamExternData *prm;
246
0
    ParamExternData prmdata;
247
0
    Oid     typeOid;
248
0
    int16   typLen;
249
0
    bool    typByVal;
250
251
    /* give hook a chance in case parameter is dynamic */
252
0
    if (paramLI->paramFetch != NULL)
253
0
      prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
254
0
    else
255
0
      prm = &paramLI->params[i];
256
257
0
    typeOid = prm->ptype;
258
259
    /* Write type OID. */
260
0
    memcpy(*start_address, &typeOid, sizeof(Oid));
261
0
    *start_address += sizeof(Oid);
262
263
    /* Write flags. */
264
0
    memcpy(*start_address, &prm->pflags, sizeof(uint16));
265
0
    *start_address += sizeof(uint16);
266
267
    /* Write datum/isnull. */
268
0
    if (OidIsValid(typeOid))
269
0
      get_typlenbyval(typeOid, &typLen, &typByVal);
270
0
    else
271
0
    {
272
      /* If no type OID, assume by-value, like copyParamList does. */
273
0
      typLen = sizeof(Datum);
274
0
      typByVal = true;
275
0
    }
276
0
    datumSerialize(prm->value, prm->isnull, typByVal, typLen,
277
0
             start_address);
278
0
  }
279
0
}
280
281
/*
282
 * Copy a ParamListInfo structure.
283
 *
284
 * The result is allocated in CurrentMemoryContext.
285
 *
286
 * Note: the intent of this function is to make a static, self-contained
287
 * set of parameter values.  If dynamic parameter hooks are present, we
288
 * intentionally do not copy them into the result.  Rather, we forcibly
289
 * instantiate all available parameter values and copy the datum values.
290
 */
291
ParamListInfo
292
RestoreParamList(char **start_address)
293
0
{
294
0
  ParamListInfo paramLI;
295
0
  int     nparams;
296
297
0
  memcpy(&nparams, *start_address, sizeof(int));
298
0
  *start_address += sizeof(int);
299
300
0
  paramLI = makeParamList(nparams);
301
302
0
  for (int i = 0; i < nparams; i++)
303
0
  {
304
0
    ParamExternData *prm = &paramLI->params[i];
305
306
    /* Read type OID. */
307
0
    memcpy(&prm->ptype, *start_address, sizeof(Oid));
308
0
    *start_address += sizeof(Oid);
309
310
    /* Read flags. */
311
0
    memcpy(&prm->pflags, *start_address, sizeof(uint16));
312
0
    *start_address += sizeof(uint16);
313
314
    /* Read datum/isnull. */
315
0
    prm->value = datumRestore(start_address, &prm->isnull);
316
0
  }
317
318
0
  return paramLI;
319
0
}
320
321
/*
322
 * BuildParamLogString
323
 *    Return a string that represents the parameter list, for logging.
324
 *
325
 * If caller already knows textual representations for some parameters, it can
326
 * pass an array of exactly params->numParams values as knownTextValues, which
327
 * can contain NULLs for any unknown individual values.  NULL can be given if
328
 * no parameters are known.
329
 *
330
 * If maxlen is >= 0, that's the maximum number of bytes of any one
331
 * parameter value to be printed; an ellipsis is added if the string is
332
 * longer.  (Added quotes are not considered in this calculation.)
333
 */
334
char *
335
BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen)
336
0
{
337
0
  MemoryContext tmpCxt,
338
0
        oldCxt;
339
0
  StringInfoData buf;
340
341
  /*
342
   * NB: think not of returning params->paramValuesStr!  It may have been
343
   * generated with a different maxlen, and so be unsuitable.  Besides that,
344
   * this is the function used to create that string.
345
   */
346
347
  /*
348
   * No work if the param fetch hook is in use.  Also, it's not possible to
349
   * do this in an aborted transaction.  (It might be possible to improve on
350
   * this last point when some knownTextValues exist, but it seems tricky.)
351
   */
352
0
  if (params->paramFetch != NULL ||
353
0
    IsAbortedTransactionBlockState())
354
0
    return NULL;
355
356
  /* Initialize the output stringinfo, in caller's memory context */
357
0
  initStringInfo(&buf);
358
359
  /* Use a temporary context to call output functions, just in case */
360
0
  tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
361
0
                   "BuildParamLogString",
362
0
                   ALLOCSET_DEFAULT_SIZES);
363
0
  oldCxt = MemoryContextSwitchTo(tmpCxt);
364
365
0
  for (int paramno = 0; paramno < params->numParams; paramno++)
366
0
  {
367
0
    ParamExternData *param = &params->params[paramno];
368
369
0
    appendStringInfo(&buf,
370
0
             "%s$%d = ",
371
0
             paramno > 0 ? ", " : "",
372
0
             paramno + 1);
373
374
0
    if (param->isnull || !OidIsValid(param->ptype))
375
0
      appendStringInfoString(&buf, "NULL");
376
0
    else
377
0
    {
378
0
      if (knownTextValues != NULL && knownTextValues[paramno] != NULL)
379
0
        appendStringInfoStringQuoted(&buf, knownTextValues[paramno],
380
0
                       maxlen);
381
0
      else
382
0
      {
383
0
        Oid     typoutput;
384
0
        bool    typisvarlena;
385
0
        char     *pstring;
386
387
0
        getTypeOutputInfo(param->ptype, &typoutput, &typisvarlena);
388
0
        pstring = OidOutputFunctionCall(typoutput, param->value);
389
0
        appendStringInfoStringQuoted(&buf, pstring, maxlen);
390
0
      }
391
0
    }
392
0
  }
393
394
0
  MemoryContextSwitchTo(oldCxt);
395
0
  MemoryContextDelete(tmpCxt);
396
397
0
  return buf.data;
398
0
}
399
400
/*
401
 * ParamsErrorCallback - callback for printing parameters in error context
402
 *
403
 * Note that this is a no-op unless BuildParamLogString has been called
404
 * beforehand.
405
 */
406
void
407
ParamsErrorCallback(void *arg)
408
0
{
409
0
  ParamsErrorCbData *data = (ParamsErrorCbData *) arg;
410
411
0
  if (data == NULL ||
412
0
    data->params == NULL ||
413
0
    data->params->paramValuesStr == NULL)
414
0
    return;
415
416
0
  if (data->portalName && data->portalName[0] != '\0')
417
0
    errcontext("portal \"%s\" with parameters: %s",
418
0
           data->portalName, data->params->paramValuesStr);
419
0
  else
420
0
    errcontext("unnamed portal with parameters: %s",
421
0
           data->params->paramValuesStr);
422
0
}