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-2024, 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 160058 : makeParamList(int numParams)
45 : {
46 : ParamListInfo retval;
47 : Size size;
48 :
49 160058 : size = offsetof(ParamListInfoData, params) +
50 : numParams * sizeof(ParamExternData);
51 :
52 160058 : retval = (ParamListInfo) palloc(size);
53 160058 : retval->paramFetch = NULL;
54 160058 : retval->paramFetchArg = NULL;
55 160058 : retval->paramCompile = NULL;
56 160058 : retval->paramCompileArg = NULL;
57 160058 : retval->parserSetup = paramlist_parser_setup;
58 160058 : retval->parserSetupArg = (void *) retval;
59 160058 : retval->paramValuesStr = NULL;
60 160058 : retval->numParams = numParams;
61 :
62 160058 : 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 3356 : copyParamList(ParamListInfo from)
79 : {
80 : ParamListInfo retval;
81 :
82 3356 : if (from == NULL || from->numParams <= 0)
83 1938 : return NULL;
84 :
85 1418 : retval = makeParamList(from->numParams);
86 :
87 6736 : for (int i = 0; i < from->numParams; i++)
88 : {
89 : ParamExternData *oprm;
90 5318 : 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 5318 : if (from->paramFetch != NULL)
97 4154 : oprm = from->paramFetch(from, i + 1, false, &prmdata);
98 : else
99 1164 : oprm = &from->params[i];
100 :
101 : /* flat-copy the parameter info */
102 5318 : *nprm = *oprm;
103 :
104 : /* need datumCopy in case it's a pass-by-reference datatype */
105 5318 : if (nprm->isnull || !OidIsValid(nprm->ptype))
106 3434 : continue;
107 1884 : get_typlenbyval(nprm->ptype, &typLen, &typByVal);
108 1884 : nprm->value = datumCopy(nprm->value, typByVal, typLen);
109 : }
110 :
111 1418 : 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 742 : EstimateParamListSpace(ParamListInfo paramLI)
168 : {
169 : int i;
170 742 : Size sz = sizeof(int);
171 :
172 742 : if (paramLI == NULL || paramLI->numParams <= 0)
173 670 : return sz;
174 :
175 228 : for (i = 0; i < paramLI->numParams; i++)
176 : {
177 : ParamExternData *prm;
178 : ParamExternData prmdata;
179 : Oid typeOid;
180 : int16 typLen;
181 : bool typByVal;
182 :
183 : /* give hook a chance in case parameter is dynamic */
184 156 : if (paramLI->paramFetch != NULL)
185 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
186 : else
187 156 : prm = ¶mLI->params[i];
188 :
189 156 : typeOid = prm->ptype;
190 :
191 156 : sz = add_size(sz, sizeof(Oid)); /* space for type OID */
192 156 : sz = add_size(sz, sizeof(uint16)); /* space for pflags */
193 :
194 : /* space for datum/isnull */
195 156 : if (OidIsValid(typeOid))
196 156 : get_typlenbyval(typeOid, &typLen, &typByVal);
197 : else
198 : {
199 : /* If no type OID, assume by-value, like copyParamList does. */
200 0 : typLen = sizeof(Datum);
201 0 : typByVal = true;
202 : }
203 156 : sz = add_size(sz,
204 156 : datumEstimateSpace(prm->value, prm->isnull, typByVal, typLen));
205 : }
206 :
207 72 : return sz;
208 : }
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 742 : SerializeParamList(ParamListInfo paramLI, char **start_address)
230 : {
231 : int nparams;
232 : int i;
233 :
234 : /* Write number of parameters. */
235 742 : if (paramLI == NULL || paramLI->numParams <= 0)
236 670 : nparams = 0;
237 : else
238 72 : nparams = paramLI->numParams;
239 742 : memcpy(*start_address, &nparams, sizeof(int));
240 742 : *start_address += sizeof(int);
241 :
242 : /* Write each parameter in turn. */
243 898 : for (i = 0; i < nparams; i++)
244 : {
245 : ParamExternData *prm;
246 : ParamExternData prmdata;
247 : Oid typeOid;
248 : int16 typLen;
249 : bool typByVal;
250 :
251 : /* give hook a chance in case parameter is dynamic */
252 156 : if (paramLI->paramFetch != NULL)
253 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
254 : else
255 156 : prm = ¶mLI->params[i];
256 :
257 156 : typeOid = prm->ptype;
258 :
259 : /* Write type OID. */
260 156 : memcpy(*start_address, &typeOid, sizeof(Oid));
261 156 : *start_address += sizeof(Oid);
262 :
263 : /* Write flags. */
264 156 : memcpy(*start_address, &prm->pflags, sizeof(uint16));
265 156 : *start_address += sizeof(uint16);
266 :
267 : /* Write datum/isnull. */
268 156 : if (OidIsValid(typeOid))
269 156 : get_typlenbyval(typeOid, &typLen, &typByVal);
270 : else
271 : {
272 : /* If no type OID, assume by-value, like copyParamList does. */
273 0 : typLen = sizeof(Datum);
274 0 : typByVal = true;
275 : }
276 156 : datumSerialize(prm->value, prm->isnull, typByVal, typLen,
277 : start_address);
278 : }
279 742 : }
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 2514 : RestoreParamList(char **start_address)
293 : {
294 : ParamListInfo paramLI;
295 : int nparams;
296 :
297 2514 : memcpy(&nparams, *start_address, sizeof(int));
298 2514 : *start_address += sizeof(int);
299 :
300 2514 : paramLI = makeParamList(nparams);
301 :
302 2706 : for (int i = 0; i < nparams; i++)
303 : {
304 192 : ParamExternData *prm = ¶mLI->params[i];
305 :
306 : /* Read type OID. */
307 192 : memcpy(&prm->ptype, *start_address, sizeof(Oid));
308 192 : *start_address += sizeof(Oid);
309 :
310 : /* Read flags. */
311 192 : memcpy(&prm->pflags, *start_address, sizeof(uint16));
312 192 : *start_address += sizeof(uint16);
313 :
314 : /* Read datum/isnull. */
315 192 : prm->value = datumRestore(start_address, &prm->isnull);
316 : }
317 :
318 2514 : return paramLI;
319 : }
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 4646 : BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen)
336 : {
337 : MemoryContext tmpCxt,
338 : oldCxt;
339 : 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 9292 : if (params->paramFetch != NULL ||
353 4646 : IsAbortedTransactionBlockState())
354 0 : return NULL;
355 :
356 : /* Initialize the output stringinfo, in caller's memory context */
357 4646 : initStringInfo(&buf);
358 :
359 : /* Use a temporary context to call output functions, just in case */
360 4646 : tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
361 : "BuildParamLogString",
362 : ALLOCSET_DEFAULT_SIZES);
363 4646 : oldCxt = MemoryContextSwitchTo(tmpCxt);
364 :
365 12668 : for (int paramno = 0; paramno < params->numParams; paramno++)
366 : {
367 8022 : ParamExternData *param = ¶ms->params[paramno];
368 :
369 8022 : appendStringInfo(&buf,
370 : "%s$%d = ",
371 : paramno > 0 ? ", " : "",
372 : paramno + 1);
373 :
374 8022 : if (param->isnull || !OidIsValid(param->ptype))
375 10 : appendStringInfoString(&buf, "NULL");
376 : else
377 : {
378 8012 : if (knownTextValues != NULL && knownTextValues[paramno] != NULL)
379 12 : appendStringInfoStringQuoted(&buf, knownTextValues[paramno],
380 : maxlen);
381 : else
382 : {
383 : Oid typoutput;
384 : bool typisvarlena;
385 : char *pstring;
386 :
387 8000 : getTypeOutputInfo(param->ptype, &typoutput, &typisvarlena);
388 8000 : pstring = OidOutputFunctionCall(typoutput, param->value);
389 8000 : appendStringInfoStringQuoted(&buf, pstring, maxlen);
390 : }
391 : }
392 : }
393 :
394 4646 : MemoryContextSwitchTo(oldCxt);
395 4646 : MemoryContextDelete(tmpCxt);
396 :
397 4646 : return buf.data;
398 : }
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 52 : ParamsErrorCallback(void *arg)
408 : {
409 52 : ParamsErrorCbData *data = (ParamsErrorCbData *) arg;
410 :
411 52 : if (data == NULL ||
412 52 : data->params == NULL ||
413 36 : data->params->paramValuesStr == NULL)
414 44 : return;
415 :
416 8 : if (data->portalName && data->portalName[0] != '\0')
417 0 : errcontext("portal \"%s\" with parameters: %s",
418 0 : data->portalName, data->params->paramValuesStr);
419 : else
420 8 : errcontext("unnamed portal with parameters: %s",
421 8 : data->params->paramValuesStr);
422 : }
|