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-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
1858 peter@eisentraut.org 44 :CBC 74900 : makeParamList(int numParams)
45 : : {
46 : : ParamListInfo retval;
47 : : Size size;
48 : :
49 : 74900 : size = offsetof(ParamListInfoData, params) +
50 : : numParams * sizeof(ParamExternData);
51 : :
52 : 74900 : retval = (ParamListInfo) palloc(size);
53 : 74900 : retval->paramFetch = NULL;
54 : 74900 : retval->paramFetchArg = NULL;
55 : 74900 : retval->paramCompile = NULL;
56 : 74900 : retval->paramCompileArg = NULL;
1402 tgl@sss.pgh.pa.us 57 : 74900 : retval->parserSetup = paramlist_parser_setup;
58 : 74900 : retval->parserSetupArg = (void *) retval;
1586 alvherre@alvh.no-ip. 59 : 74900 : retval->paramValuesStr = NULL;
1858 peter@eisentraut.org 60 : 74900 : retval->numParams = numParams;
61 : :
62 : 74900 : 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
7195 tgl@sss.pgh.pa.us 78 : 1618 : copyParamList(ParamListInfo from)
79 : : {
80 : : ParamListInfo retval;
81 : :
6567 82 [ + + - + ]: 1618 : if (from == NULL || from->numParams <= 0)
7195 83 : 907 : return NULL;
84 : :
1858 peter@eisentraut.org 85 : 711 : retval = makeParamList(from->numParams);
86 : :
87 [ + + ]: 3491 : for (int i = 0; i < from->numParams; i++)
88 : : {
89 : : ParamExternData *oprm;
5275 tgl@sss.pgh.pa.us 90 : 2780 : 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 */
2306 96 [ + + ]: 2780 : if (from->paramFetch != NULL)
97 : 2209 : oprm = from->paramFetch(from, i + 1, false, &prmdata);
98 : : else
99 : 571 : oprm = &from->params[i];
100 : :
101 : : /* flat-copy the parameter info */
5275 102 : 2780 : *nprm = *oprm;
103 : :
104 : : /* need datumCopy in case it's a pass-by-reference datatype */
105 [ + + - + ]: 2780 : if (nprm->isnull || !OidIsValid(nprm->ptype))
6567 106 : 1837 : continue;
5275 107 : 943 : get_typlenbyval(nprm->ptype, &typLen, &typByVal);
108 : 943 : nprm->value = datumCopy(nprm->value, typByVal, typLen);
109 : : }
110 : :
7195 111 : 711 : 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
1402 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)
1402 tgl@sss.pgh.pa.us 141 :UBC 0 : return NULL;
142 : :
143 : : /* give hook a chance in case parameter is dynamic */
1402 tgl@sss.pgh.pa.us 144 [ - + ]:CBC 576 : if (paramLI->paramFetch != NULL)
1402 tgl@sss.pgh.pa.us 145 :UBC 0 : prm = paramLI->paramFetch(paramLI, paramno, false, &prmdata);
146 : : else
1402 tgl@sss.pgh.pa.us 147 :CBC 576 : prm = ¶mLI->params[paramno - 1];
148 : :
149 [ - + ]: 576 : if (!OidIsValid(prm->ptype))
1402 tgl@sss.pgh.pa.us 150 :UBC 0 : return NULL;
151 : :
1402 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
3121 rhaas@postgresql.org 167 : 332 : EstimateParamListSpace(ParamListInfo paramLI)
168 : : {
169 : : int i;
2866 170 : 332 : Size sz = sizeof(int);
171 : :
3121 172 [ + + - + ]: 332 : if (paramLI == NULL || paramLI->numParams <= 0)
173 : 314 : return sz;
174 : :
175 [ + + ]: 60 : 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 */
2306 tgl@sss.pgh.pa.us 184 [ - + ]: 42 : if (paramLI->paramFetch != NULL)
2306 tgl@sss.pgh.pa.us 185 :UBC 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
186 : : else
2306 tgl@sss.pgh.pa.us 187 :CBC 42 : prm = ¶mLI->params[i];
188 : :
189 : 42 : typeOid = prm->ptype;
190 : :
2866 rhaas@postgresql.org 191 : 42 : sz = add_size(sz, sizeof(Oid)); /* space for type OID */
2489 tgl@sss.pgh.pa.us 192 : 42 : sz = add_size(sz, sizeof(uint16)); /* space for pflags */
193 : :
194 : : /* space for datum/isnull */
3086 rhaas@postgresql.org 195 [ + - ]: 42 : if (OidIsValid(typeOid))
196 : 42 : get_typlenbyval(typeOid, &typLen, &typByVal);
197 : : else
198 : : {
199 : : /* If no type OID, assume by-value, like copyParamList does. */
3121 rhaas@postgresql.org 200 :UBC 0 : typLen = sizeof(Datum);
201 : 0 : typByVal = true;
202 : : }
3121 rhaas@postgresql.org 203 :CBC 42 : sz = add_size(sz,
2489 tgl@sss.pgh.pa.us 204 : 42 : datumEstimateSpace(prm->value, prm->isnull, typByVal, typLen));
205 : : }
206 : :
3121 rhaas@postgresql.org 207 : 18 : 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 : 332 : SerializeParamList(ParamListInfo paramLI, char **start_address)
230 : : {
231 : : int nparams;
232 : : int i;
233 : :
234 : : /* Write number of parameters. */
235 [ + + - + ]: 332 : if (paramLI == NULL || paramLI->numParams <= 0)
236 : 314 : nparams = 0;
237 : : else
238 : 18 : nparams = paramLI->numParams;
239 : 332 : memcpy(*start_address, &nparams, sizeof(int));
240 : 332 : *start_address += sizeof(int);
241 : :
242 : : /* Write each parameter in turn. */
243 [ + + ]: 374 : 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 */
2306 tgl@sss.pgh.pa.us 252 [ - + ]: 42 : if (paramLI->paramFetch != NULL)
2306 tgl@sss.pgh.pa.us 253 :UBC 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
254 : : else
2306 tgl@sss.pgh.pa.us 255 :CBC 42 : prm = ¶mLI->params[i];
256 : :
257 : 42 : typeOid = prm->ptype;
258 : :
259 : : /* Write type OID. */
3086 rhaas@postgresql.org 260 : 42 : memcpy(*start_address, &typeOid, sizeof(Oid));
3121 261 : 42 : *start_address += sizeof(Oid);
262 : :
263 : : /* Write flags. */
264 : 42 : memcpy(*start_address, &prm->pflags, sizeof(uint16));
265 : 42 : *start_address += sizeof(uint16);
266 : :
267 : : /* Write datum/isnull. */
3086 268 [ + - ]: 42 : if (OidIsValid(typeOid))
269 : 42 : get_typlenbyval(typeOid, &typLen, &typByVal);
270 : : else
271 : : {
272 : : /* If no type OID, assume by-value, like copyParamList does. */
3121 rhaas@postgresql.org 273 :UBC 0 : typLen = sizeof(Datum);
274 : 0 : typByVal = true;
275 : : }
3121 rhaas@postgresql.org 276 :CBC 42 : datumSerialize(prm->value, prm->isnull, typByVal, typLen,
277 : : start_address);
278 : : }
279 : 332 : }
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 : 1232 : RestoreParamList(char **start_address)
293 : : {
294 : : ParamListInfo paramLI;
295 : : int nparams;
296 : :
297 : 1232 : memcpy(&nparams, *start_address, sizeof(int));
298 : 1232 : *start_address += sizeof(int);
299 : :
1858 peter@eisentraut.org 300 : 1232 : paramLI = makeParamList(nparams);
301 : :
302 [ + + ]: 1328 : for (int i = 0; i < nparams; i++)
303 : : {
3121 rhaas@postgresql.org 304 : 96 : ParamExternData *prm = ¶mLI->params[i];
305 : :
306 : : /* Read type OID. */
307 : 96 : memcpy(&prm->ptype, *start_address, sizeof(Oid));
308 : 96 : *start_address += sizeof(Oid);
309 : :
310 : : /* Read flags. */
311 : 96 : memcpy(&prm->pflags, *start_address, sizeof(uint16));
312 : 96 : *start_address += sizeof(uint16);
313 : :
314 : : /* Read datum/isnull. */
315 : 96 : prm->value = datumRestore(start_address, &prm->isnull);
316 : : }
317 : :
318 : 1232 : 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 *
1586 alvherre@alvh.no-ip. 335 : 2307 : 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 [ + - - + ]: 4614 : if (params->paramFetch != NULL ||
353 : 2307 : IsAbortedTransactionBlockState())
1586 alvherre@alvh.no-ip. 354 :UBC 0 : return NULL;
355 : :
356 : : /* Initialize the output stringinfo, in caller's memory context */
1586 alvherre@alvh.no-ip. 357 :CBC 2307 : initStringInfo(&buf);
358 : :
359 : : /* Use a temporary context to call output functions, just in case */
360 : 2307 : tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
361 : : "BuildParamLogString",
362 : : ALLOCSET_DEFAULT_SIZES);
363 : 2307 : oldCxt = MemoryContextSwitchTo(tmpCxt);
364 : :
365 [ + + ]: 6300 : for (int paramno = 0; paramno < params->numParams; paramno++)
366 : : {
367 : 3993 : ParamExternData *param = ¶ms->params[paramno];
368 : :
369 [ + + ]: 3993 : appendStringInfo(&buf,
370 : : "%s$%d = ",
371 : : paramno > 0 ? ", " : "",
372 : : paramno + 1);
373 : :
374 [ + + - + ]: 3993 : if (param->isnull || !OidIsValid(param->ptype))
375 : 5 : appendStringInfoString(&buf, "NULL");
376 : : else
377 : : {
378 [ + + + - ]: 3988 : if (knownTextValues != NULL && knownTextValues[paramno] != NULL)
379 : 6 : appendStringInfoStringQuoted(&buf, knownTextValues[paramno],
380 : : maxlen);
381 : : else
382 : : {
383 : : Oid typoutput;
384 : : bool typisvarlena;
385 : : char *pstring;
386 : :
387 : 3982 : getTypeOutputInfo(param->ptype, &typoutput, &typisvarlena);
388 : 3982 : pstring = OidOutputFunctionCall(typoutput, param->value);
389 : 3982 : appendStringInfoStringQuoted(&buf, pstring, maxlen);
390 : : }
391 : : }
392 : : }
393 : :
394 : 2307 : MemoryContextSwitchTo(oldCxt);
395 : 2307 : MemoryContextDelete(tmpCxt);
396 : :
397 : 2307 : 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 : 26 : ParamsErrorCallback(void *arg)
408 : : {
409 : 26 : ParamsErrorCbData *data = (ParamsErrorCbData *) arg;
410 : :
411 [ + - ]: 26 : if (data == NULL ||
412 [ + + ]: 26 : data->params == NULL ||
413 [ + + ]: 18 : data->params->paramValuesStr == NULL)
414 : 22 : return;
415 : :
416 [ + - - + ]: 4 : if (data->portalName && data->portalName[0] != '\0')
1308 peter@eisentraut.org 417 :UBC 0 : errcontext("portal \"%s\" with parameters: %s",
1586 alvherre@alvh.no-ip. 418 : 0 : data->portalName, data->params->paramValuesStr);
419 : : else
1308 peter@eisentraut.org 420 :CBC 4 : errcontext("unnamed portal with parameters: %s",
1586 alvherre@alvh.no-ip. 421 : 4 : data->params->paramValuesStr);
422 : : }
|