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