Age Owner Branch data TLA Line data Source code
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
2469 peter@eisentraut.org 44 :CBC 77301 : makeParamList(int numParams)
45 : : {
46 : : ParamListInfo retval;
47 : : Size size;
48 : :
49 : 77301 : size = offsetof(ParamListInfoData, params) +
50 : : numParams * sizeof(ParamExternData);
51 : :
52 : 77301 : retval = (ParamListInfo) palloc(size);
53 : 77301 : retval->paramFetch = NULL;
54 : 77301 : retval->paramFetchArg = NULL;
55 : 77301 : retval->paramCompile = NULL;
56 : 77301 : retval->paramCompileArg = NULL;
2013 tgl@sss.pgh.pa.us 57 : 77301 : retval->parserSetup = paramlist_parser_setup;
383 peter@eisentraut.org 58 : 77301 : retval->parserSetupArg = retval;
2197 alvherre@alvh.no-ip. 59 : 77301 : retval->paramValuesStr = NULL;
2469 peter@eisentraut.org 60 : 77301 : retval->numParams = numParams;
61 : :
62 : 77301 : return retval;
63 : : }
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
7806 tgl@sss.pgh.pa.us 78 : 2544 : copyParamList(ParamListInfo from)
79 : : {
80 : : ParamListInfo retval;
81 : :
7178 82 [ + + - + ]: 2544 : if (from == NULL || from->numParams <= 0)
7806 83 : 1843 : return NULL;
84 : :
2469 peter@eisentraut.org 85 : 701 : retval = makeParamList(from->numParams);
86 : :
87 [ + + ]: 3362 : for (int i = 0; i < from->numParams; i++)
88 : : {
89 : : ParamExternData *oprm;
5886 tgl@sss.pgh.pa.us 90 : 2661 : ParamExternData *nprm = &retval->params[i];
91 : : ParamExternData prmdata;
92 : : int16 typLen;
93 : : bool typByVal;
94 : :
95 : : /* give hook a chance in case parameter is dynamic */
2917 96 [ + + ]: 2661 : if (from->paramFetch != NULL)
97 : 2086 : oprm = from->paramFetch(from, i + 1, false, &prmdata);
98 : : else
99 : 575 : oprm = &from->params[i];
100 : :
101 : : /* flat-copy the parameter info */
5886 102 : 2661 : *nprm = *oprm;
103 : :
104 : : /* need datumCopy in case it's a pass-by-reference datatype */
105 [ + + - + ]: 2661 : if (nprm->isnull || !OidIsValid(nprm->ptype))
7178 106 : 1728 : continue;
5886 107 : 933 : get_typlenbyval(nprm->ptype, &typLen, &typByVal);
108 : 933 : nprm->value = datumCopy(nprm->value, typByVal, typLen);
109 : : }
110 : :
7806 111 : 701 : return retval;
112 : : }
113 : :
114 : :
115 : : /*
116 : : * Set up to parse a query containing references to parameters
117 : : * sourced from a ParamListInfo.
118 : : */
119 : : static void
2013 120 : 291 : paramlist_parser_setup(ParseState *pstate, void *arg)
121 : : {
122 : 291 : pstate->p_paramref_hook = paramlist_param_ref;
123 : : /* no need to use p_coerce_param_hook */
124 : 291 : pstate->p_ref_hook_state = arg;
125 : 291 : }
126 : :
127 : : /*
128 : : * Transform a ParamRef using parameter type data from a ParamListInfo.
129 : : */
130 : : static Node *
131 : 576 : paramlist_param_ref(ParseState *pstate, ParamRef *pref)
132 : : {
133 : 576 : ParamListInfo paramLI = (ParamListInfo) pstate->p_ref_hook_state;
134 : 576 : int paramno = pref->number;
135 : : ParamExternData *prm;
136 : : ParamExternData prmdata;
137 : : Param *param;
138 : :
139 : : /* check parameter number is valid */
140 [ + - - + ]: 576 : if (paramno <= 0 || paramno > paramLI->numParams)
2013 tgl@sss.pgh.pa.us 141 :UBC 0 : return NULL;
142 : :
143 : : /* give hook a chance in case parameter is dynamic */
2013 tgl@sss.pgh.pa.us 144 [ - + ]:CBC 576 : if (paramLI->paramFetch != NULL)
2013 tgl@sss.pgh.pa.us 145 :UBC 0 : prm = paramLI->paramFetch(paramLI, paramno, false, &prmdata);
146 : : else
2013 tgl@sss.pgh.pa.us 147 :CBC 576 : prm = ¶mLI->params[paramno - 1];
148 : :
149 [ - + ]: 576 : if (!OidIsValid(prm->ptype))
2013 tgl@sss.pgh.pa.us 150 :UBC 0 : return NULL;
151 : :
2013 tgl@sss.pgh.pa.us 152 :CBC 576 : param = makeNode(Param);
153 : 576 : param->paramkind = PARAM_EXTERN;
154 : 576 : param->paramid = paramno;
155 : 576 : param->paramtype = prm->ptype;
156 : 576 : param->paramtypmod = -1;
157 : 576 : param->paramcollid = get_typcollation(param->paramtype);
158 : 576 : param->location = pref->location;
159 : :
160 : 576 : return (Node *) param;
161 : : }
162 : :
163 : : /*
164 : : * Estimate the amount of space required to serialize a ParamListInfo.
165 : : */
166 : : Size
3732 rhaas@postgresql.org 167 : 378 : EstimateParamListSpace(ParamListInfo paramLI)
168 : : {
3477 169 : 378 : Size sz = sizeof(int);
170 : :
3732 171 [ + + - + ]: 378 : if (paramLI == NULL || paramLI->numParams <= 0)
172 : 360 : return sz;
173 : :
21 peter@eisentraut.org 174 [ + + ]:GNC 60 : for (int 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 */
2917 tgl@sss.pgh.pa.us 183 [ - + ]:CBC 42 : if (paramLI->paramFetch != NULL)
2917 tgl@sss.pgh.pa.us 184 :UBC 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
185 : : else
2917 tgl@sss.pgh.pa.us 186 :CBC 42 : prm = ¶mLI->params[i];
187 : :
188 : 42 : typeOid = prm->ptype;
189 : :
3477 rhaas@postgresql.org 190 : 42 : sz = add_size(sz, sizeof(Oid)); /* space for type OID */
3100 tgl@sss.pgh.pa.us 191 : 42 : sz = add_size(sz, sizeof(uint16)); /* space for pflags */
192 : :
193 : : /* space for datum/isnull */
3697 rhaas@postgresql.org 194 [ + - ]: 42 : if (OidIsValid(typeOid))
195 : 42 : get_typlenbyval(typeOid, &typLen, &typByVal);
196 : : else
197 : : {
198 : : /* If no type OID, assume by-value, like copyParamList does. */
3732 rhaas@postgresql.org 199 :UBC 0 : typLen = sizeof(Datum);
200 : 0 : typByVal = true;
201 : : }
3732 rhaas@postgresql.org 202 :CBC 42 : sz = add_size(sz,
3100 tgl@sss.pgh.pa.us 203 : 42 : datumEstimateSpace(prm->value, prm->isnull, typByVal, typLen));
204 : : }
205 : :
3732 rhaas@postgresql.org 206 : 18 : 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 : 378 : SerializeParamList(ParamListInfo paramLI, char **start_address)
229 : : {
230 : : int nparams;
231 : :
232 : : /* Write number of parameters. */
233 [ + + - + ]: 378 : if (paramLI == NULL || paramLI->numParams <= 0)
234 : 360 : nparams = 0;
235 : : else
236 : 18 : nparams = paramLI->numParams;
237 : 378 : memcpy(*start_address, &nparams, sizeof(int));
238 : 378 : *start_address += sizeof(int);
239 : :
240 : : /* Write each parameter in turn. */
21 peter@eisentraut.org 241 [ + + ]:GNC 420 : for (int i = 0; i < nparams; i++)
242 : : {
243 : : ParamExternData *prm;
244 : : ParamExternData prmdata;
245 : : Oid typeOid;
246 : : int16 typLen;
247 : : bool typByVal;
248 : :
249 : : /* give hook a chance in case parameter is dynamic */
2917 tgl@sss.pgh.pa.us 250 [ - + ]:CBC 42 : if (paramLI->paramFetch != NULL)
2917 tgl@sss.pgh.pa.us 251 :UBC 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
252 : : else
2917 tgl@sss.pgh.pa.us 253 :CBC 42 : prm = ¶mLI->params[i];
254 : :
255 : 42 : typeOid = prm->ptype;
256 : :
257 : : /* Write type OID. */
3697 rhaas@postgresql.org 258 : 42 : memcpy(*start_address, &typeOid, sizeof(Oid));
3732 259 : 42 : *start_address += sizeof(Oid);
260 : :
261 : : /* Write flags. */
262 : 42 : memcpy(*start_address, &prm->pflags, sizeof(uint16));
263 : 42 : *start_address += sizeof(uint16);
264 : :
265 : : /* Write datum/isnull. */
3697 266 [ + - ]: 42 : if (OidIsValid(typeOid))
267 : 42 : get_typlenbyval(typeOid, &typLen, &typByVal);
268 : : else
269 : : {
270 : : /* If no type OID, assume by-value, like copyParamList does. */
3732 rhaas@postgresql.org 271 :UBC 0 : typLen = sizeof(Datum);
272 : 0 : typByVal = true;
273 : : }
3732 rhaas@postgresql.org 274 :CBC 42 : datumSerialize(prm->value, prm->isnull, typByVal, typLen,
275 : : start_address);
276 : : }
277 : 378 : }
278 : :
279 : : /*
280 : : * Copy a ParamListInfo structure.
281 : : *
282 : : * The result is allocated in CurrentMemoryContext.
283 : : *
284 : : * Note: the intent of this function is to make a static, self-contained
285 : : * set of parameter values. If dynamic parameter hooks are present, we
286 : : * intentionally do not copy them into the result. Rather, we forcibly
287 : : * instantiate all available parameter values and copy the datum values.
288 : : */
289 : : ParamListInfo
290 : 1332 : RestoreParamList(char **start_address)
291 : : {
292 : : ParamListInfo paramLI;
293 : : int nparams;
294 : :
295 : 1332 : memcpy(&nparams, *start_address, sizeof(int));
296 : 1332 : *start_address += sizeof(int);
297 : :
2469 peter@eisentraut.org 298 : 1332 : paramLI = makeParamList(nparams);
299 : :
300 [ + + ]: 1428 : for (int i = 0; i < nparams; i++)
301 : : {
3732 rhaas@postgresql.org 302 : 96 : ParamExternData *prm = ¶mLI->params[i];
303 : :
304 : : /* Read type OID. */
305 : 96 : memcpy(&prm->ptype, *start_address, sizeof(Oid));
306 : 96 : *start_address += sizeof(Oid);
307 : :
308 : : /* Read flags. */
309 : 96 : memcpy(&prm->pflags, *start_address, sizeof(uint16));
310 : 96 : *start_address += sizeof(uint16);
311 : :
312 : : /* Read datum/isnull. */
313 : 96 : prm->value = datumRestore(start_address, &prm->isnull);
314 : : }
315 : :
316 : 1332 : return paramLI;
317 : : }
318 : :
319 : : /*
320 : : * BuildParamLogString
321 : : * Return a string that represents the parameter list, for logging.
322 : : *
323 : : * If caller already knows textual representations for some parameters, it can
324 : : * pass an array of exactly params->numParams values as knownTextValues, which
325 : : * can contain NULLs for any unknown individual values. NULL can be given if
326 : : * no parameters are known.
327 : : *
328 : : * If maxlen is >= 0, that's the maximum number of bytes of any one
329 : : * parameter value to be printed; an ellipsis is added if the string is
330 : : * longer. (Added quotes are not considered in this calculation.)
331 : : */
332 : : char *
2197 alvherre@alvh.no-ip. 333 : 2388 : BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen)
334 : : {
335 : : MemoryContext tmpCxt,
336 : : oldCxt;
337 : : StringInfoData buf;
338 : :
339 : : /*
340 : : * NB: think not of returning params->paramValuesStr! It may have been
341 : : * generated with a different maxlen, and so be unsuitable. Besides that,
342 : : * this is the function used to create that string.
343 : : */
344 : :
345 : : /*
346 : : * No work if the param fetch hook is in use. Also, it's not possible to
347 : : * do this in an aborted transaction. (It might be possible to improve on
348 : : * this last point when some knownTextValues exist, but it seems tricky.)
349 : : */
350 [ + - - + ]: 4776 : if (params->paramFetch != NULL ||
351 : 2388 : IsAbortedTransactionBlockState())
2197 alvherre@alvh.no-ip. 352 :UBC 0 : return NULL;
353 : :
354 : : /* Initialize the output stringinfo, in caller's memory context */
2197 alvherre@alvh.no-ip. 355 :CBC 2388 : initStringInfo(&buf);
356 : :
357 : : /* Use a temporary context to call output functions, just in case */
358 : 2388 : tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
359 : : "BuildParamLogString",
360 : : ALLOCSET_DEFAULT_SIZES);
361 : 2388 : oldCxt = MemoryContextSwitchTo(tmpCxt);
362 : :
363 [ + + ]: 6499 : for (int paramno = 0; paramno < params->numParams; paramno++)
364 : : {
365 : 4111 : ParamExternData *param = ¶ms->params[paramno];
366 : :
367 [ + + ]: 4111 : appendStringInfo(&buf,
368 : : "%s$%d = ",
369 : : paramno > 0 ? ", " : "",
370 : : paramno + 1);
371 : :
372 [ + + - + ]: 4111 : if (param->isnull || !OidIsValid(param->ptype))
373 : 5 : appendStringInfoString(&buf, "NULL");
374 : : else
375 : : {
376 [ + + + - ]: 4106 : if (knownTextValues != NULL && knownTextValues[paramno] != NULL)
377 : 6 : appendStringInfoStringQuoted(&buf, knownTextValues[paramno],
378 : : maxlen);
379 : : else
380 : : {
381 : : Oid typoutput;
382 : : bool typisvarlena;
383 : : char *pstring;
384 : :
385 : 4100 : getTypeOutputInfo(param->ptype, &typoutput, &typisvarlena);
386 : 4100 : pstring = OidOutputFunctionCall(typoutput, param->value);
387 : 4100 : appendStringInfoStringQuoted(&buf, pstring, maxlen);
388 : : }
389 : : }
390 : : }
391 : :
392 : 2388 : MemoryContextSwitchTo(oldCxt);
393 : 2388 : MemoryContextDelete(tmpCxt);
394 : :
395 : 2388 : return buf.data;
396 : : }
397 : :
398 : : /*
399 : : * ParamsErrorCallback - callback for printing parameters in error context
400 : : *
401 : : * Note that this is a no-op unless BuildParamLogString has been called
402 : : * beforehand.
403 : : */
404 : : void
405 : 58 : ParamsErrorCallback(void *arg)
406 : : {
407 : 58 : ParamsErrorCbData *data = (ParamsErrorCbData *) arg;
408 : :
409 [ + - ]: 58 : if (data == NULL ||
410 [ + + ]: 58 : data->params == NULL ||
411 [ + + ]: 27 : data->params->paramValuesStr == NULL)
412 : 54 : return;
413 : :
414 [ + - - + ]: 4 : if (data->portalName && data->portalName[0] != '\0')
1919 peter@eisentraut.org 415 :UBC 0 : errcontext("portal \"%s\" with parameters: %s",
2197 alvherre@alvh.no-ip. 416 : 0 : data->portalName, data->params->paramValuesStr);
417 : : else
1919 peter@eisentraut.org 418 :CBC 4 : errcontext("unnamed portal with parameters: %s",
2197 alvherre@alvh.no-ip. 419 : 4 : data->params->paramValuesStr);
420 : : }
|