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
44 164168 : makeParamList(int numParams)
45 : {
46 : ParamListInfo retval;
47 : Size size;
48 :
49 164168 : size = offsetof(ParamListInfoData, params) +
50 : numParams * sizeof(ParamExternData);
51 :
52 164168 : retval = (ParamListInfo) palloc(size);
53 164168 : retval->paramFetch = NULL;
54 164168 : retval->paramFetchArg = NULL;
55 164168 : retval->paramCompile = NULL;
56 164168 : retval->paramCompileArg = NULL;
57 164168 : retval->parserSetup = paramlist_parser_setup;
58 164168 : retval->parserSetupArg = retval;
59 164168 : retval->paramValuesStr = NULL;
60 164168 : retval->numParams = numParams;
61 :
62 164168 : 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
78 5168 : copyParamList(ParamListInfo from)
79 : {
80 : ParamListInfo retval;
81 :
82 5168 : if (from == NULL || from->numParams <= 0)
83 3742 : return NULL;
84 :
85 1426 : retval = makeParamList(from->numParams);
86 :
87 6752 : for (int i = 0; i < from->numParams; i++)
88 : {
89 : ParamExternData *oprm;
90 5326 : 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 */
96 5326 : if (from->paramFetch != NULL)
97 4154 : oprm = from->paramFetch(from, i + 1, false, &prmdata);
98 : else
99 1172 : oprm = &from->params[i];
100 :
101 : /* flat-copy the parameter info */
102 5326 : *nprm = *oprm;
103 :
104 : /* need datumCopy in case it's a pass-by-reference datatype */
105 5326 : if (nprm->isnull || !OidIsValid(nprm->ptype))
106 3434 : continue;
107 1892 : get_typlenbyval(nprm->ptype, &typLen, &typByVal);
108 1892 : nprm->value = datumCopy(nprm->value, typByVal, typLen);
109 : }
110 :
111 1426 : 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
120 582 : paramlist_parser_setup(ParseState *pstate, void *arg)
121 : {
122 582 : pstate->p_paramref_hook = paramlist_param_ref;
123 : /* no need to use p_coerce_param_hook */
124 582 : pstate->p_ref_hook_state = arg;
125 582 : }
126 :
127 : /*
128 : * Transform a ParamRef using parameter type data from a ParamListInfo.
129 : */
130 : static Node *
131 1152 : paramlist_param_ref(ParseState *pstate, ParamRef *pref)
132 : {
133 1152 : ParamListInfo paramLI = (ParamListInfo) pstate->p_ref_hook_state;
134 1152 : int paramno = pref->number;
135 : ParamExternData *prm;
136 : ParamExternData prmdata;
137 : Param *param;
138 :
139 : /* check parameter number is valid */
140 1152 : if (paramno <= 0 || paramno > paramLI->numParams)
141 0 : return NULL;
142 :
143 : /* give hook a chance in case parameter is dynamic */
144 1152 : if (paramLI->paramFetch != NULL)
145 0 : prm = paramLI->paramFetch(paramLI, paramno, false, &prmdata);
146 : else
147 1152 : prm = ¶mLI->params[paramno - 1];
148 :
149 1152 : if (!OidIsValid(prm->ptype))
150 0 : return NULL;
151 :
152 1152 : param = makeNode(Param);
153 1152 : param->paramkind = PARAM_EXTERN;
154 1152 : param->paramid = paramno;
155 1152 : param->paramtype = prm->ptype;
156 1152 : param->paramtypmod = -1;
157 1152 : param->paramcollid = get_typcollation(param->paramtype);
158 1152 : param->location = pref->location;
159 :
160 1152 : return (Node *) param;
161 : }
162 :
163 : /*
164 : * Estimate the amount of space required to serialize a ParamListInfo.
165 : */
166 : Size
167 754 : EstimateParamListSpace(ParamListInfo paramLI)
168 : {
169 754 : Size sz = sizeof(int);
170 :
171 754 : if (paramLI == NULL || paramLI->numParams <= 0)
172 718 : return sz;
173 :
174 120 : 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 */
183 84 : if (paramLI->paramFetch != NULL)
184 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
185 : else
186 84 : prm = ¶mLI->params[i];
187 :
188 84 : typeOid = prm->ptype;
189 :
190 84 : sz = add_size(sz, sizeof(Oid)); /* space for type OID */
191 84 : sz = add_size(sz, sizeof(uint16)); /* space for pflags */
192 :
193 : /* space for datum/isnull */
194 84 : if (OidIsValid(typeOid))
195 84 : get_typlenbyval(typeOid, &typLen, &typByVal);
196 : else
197 : {
198 : /* If no type OID, assume by-value, like copyParamList does. */
199 0 : typLen = sizeof(Datum);
200 0 : typByVal = true;
201 : }
202 84 : sz = add_size(sz,
203 84 : datumEstimateSpace(prm->value, prm->isnull, typByVal, typLen));
204 : }
205 :
206 36 : 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 754 : SerializeParamList(ParamListInfo paramLI, char **start_address)
229 : {
230 : int nparams;
231 :
232 : /* Write number of parameters. */
233 754 : if (paramLI == NULL || paramLI->numParams <= 0)
234 718 : nparams = 0;
235 : else
236 36 : nparams = paramLI->numParams;
237 754 : memcpy(*start_address, &nparams, sizeof(int));
238 754 : *start_address += sizeof(int);
239 :
240 : /* Write each parameter in turn. */
241 838 : 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 */
250 84 : if (paramLI->paramFetch != NULL)
251 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
252 : else
253 84 : prm = ¶mLI->params[i];
254 :
255 84 : typeOid = prm->ptype;
256 :
257 : /* Write type OID. */
258 84 : memcpy(*start_address, &typeOid, sizeof(Oid));
259 84 : *start_address += sizeof(Oid);
260 :
261 : /* Write flags. */
262 84 : memcpy(*start_address, &prm->pflags, sizeof(uint16));
263 84 : *start_address += sizeof(uint16);
264 :
265 : /* Write datum/isnull. */
266 84 : if (OidIsValid(typeOid))
267 84 : get_typlenbyval(typeOid, &typLen, &typByVal);
268 : else
269 : {
270 : /* If no type OID, assume by-value, like copyParamList does. */
271 0 : typLen = sizeof(Datum);
272 0 : typByVal = true;
273 : }
274 84 : datumSerialize(prm->value, prm->isnull, typByVal, typLen,
275 : start_address);
276 : }
277 754 : }
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 2662 : RestoreParamList(char **start_address)
291 : {
292 : ParamListInfo paramLI;
293 : int nparams;
294 :
295 2662 : memcpy(&nparams, *start_address, sizeof(int));
296 2662 : *start_address += sizeof(int);
297 :
298 2662 : paramLI = makeParamList(nparams);
299 :
300 2854 : for (int i = 0; i < nparams; i++)
301 : {
302 192 : ParamExternData *prm = ¶mLI->params[i];
303 :
304 : /* Read type OID. */
305 192 : memcpy(&prm->ptype, *start_address, sizeof(Oid));
306 192 : *start_address += sizeof(Oid);
307 :
308 : /* Read flags. */
309 192 : memcpy(&prm->pflags, *start_address, sizeof(uint16));
310 192 : *start_address += sizeof(uint16);
311 :
312 : /* Read datum/isnull. */
313 192 : prm->value = datumRestore(start_address, &prm->isnull);
314 : }
315 :
316 2662 : 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 *
333 4776 : 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 9552 : if (params->paramFetch != NULL ||
351 4776 : IsAbortedTransactionBlockState())
352 0 : return NULL;
353 :
354 : /* Initialize the output stringinfo, in caller's memory context */
355 4776 : initStringInfo(&buf);
356 :
357 : /* Use a temporary context to call output functions, just in case */
358 4776 : tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
359 : "BuildParamLogString",
360 : ALLOCSET_DEFAULT_SIZES);
361 4776 : oldCxt = MemoryContextSwitchTo(tmpCxt);
362 :
363 13002 : for (int paramno = 0; paramno < params->numParams; paramno++)
364 : {
365 8226 : ParamExternData *param = ¶ms->params[paramno];
366 :
367 8226 : appendStringInfo(&buf,
368 : "%s$%d = ",
369 : paramno > 0 ? ", " : "",
370 : paramno + 1);
371 :
372 8226 : if (param->isnull || !OidIsValid(param->ptype))
373 10 : appendStringInfoString(&buf, "NULL");
374 : else
375 : {
376 8216 : if (knownTextValues != NULL && knownTextValues[paramno] != NULL)
377 12 : appendStringInfoStringQuoted(&buf, knownTextValues[paramno],
378 : maxlen);
379 : else
380 : {
381 : Oid typoutput;
382 : bool typisvarlena;
383 : char *pstring;
384 :
385 8204 : getTypeOutputInfo(param->ptype, &typoutput, &typisvarlena);
386 8204 : pstring = OidOutputFunctionCall(typoutput, param->value);
387 8204 : appendStringInfoStringQuoted(&buf, pstring, maxlen);
388 : }
389 : }
390 : }
391 :
392 4776 : MemoryContextSwitchTo(oldCxt);
393 4776 : MemoryContextDelete(tmpCxt);
394 :
395 4776 : 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 122 : ParamsErrorCallback(void *arg)
406 : {
407 122 : ParamsErrorCbData *data = (ParamsErrorCbData *) arg;
408 :
409 122 : if (data == NULL ||
410 122 : data->params == NULL ||
411 60 : data->params->paramValuesStr == NULL)
412 114 : return;
413 :
414 8 : if (data->portalName && data->portalName[0] != '\0')
415 0 : errcontext("portal \"%s\" with parameters: %s",
416 0 : data->portalName, data->params->paramValuesStr);
417 : else
418 8 : errcontext("unnamed portal with parameters: %s",
419 8 : data->params->paramValuesStr);
420 : }
|